diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/packages/ticker/README.md b/packages/ticker/README.md index d7c459e..6c0f8e5 100644 --- a/packages/ticker/README.md +++ b/packages/ticker/README.md @@ -8,6 +8,23 @@ ## Usage +Create a Ticker object directly: + ```js -import * as ticker from '@pixi/ticker'; +import { Ticker } from '@pixi/ticker'; + +const ticker = new Ticker(); +ticker.start(); +``` + +Use as an Application plugin: + +```js +import { TickerPlugin } from '@pixi/ticker'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(TickerPlugin); + +const app = new Application(); +app.ticker.start(); ``` \ No newline at end of file diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/packages/ticker/README.md b/packages/ticker/README.md index d7c459e..6c0f8e5 100644 --- a/packages/ticker/README.md +++ b/packages/ticker/README.md @@ -8,6 +8,23 @@ ## Usage +Create a Ticker object directly: + ```js -import * as ticker from '@pixi/ticker'; +import { Ticker } from '@pixi/ticker'; + +const ticker = new Ticker(); +ticker.start(); +``` + +Use as an Application plugin: + +```js +import { TickerPlugin } from '@pixi/ticker'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(TickerPlugin); + +const app = new Application(); +app.ticker.start(); ``` \ No newline at end of file diff --git a/packages/ticker/src/TickerPlugin.js b/packages/ticker/src/TickerPlugin.js new file mode 100644 index 0000000..834ac48 --- /dev/null +++ b/packages/ticker/src/TickerPlugin.js @@ -0,0 +1,108 @@ +import Ticker from './Ticker'; +import { UPDATE_PRIORITY } from './const'; + +/** + * Middleware for for Application Ticker. + * @example + * import {TickerPlugin} from '@pixi/ticker'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(TickerPlugin); + * @class + * @memberof PIXI + */ +export default class TickerPlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + // Set default + options = Object.assign({ + autoStart: true, + sharedTicker: false, + }, options); + + // Create ticker setter + Object.defineProperty(this, 'ticker', + { + set(ticker) + { + if (this._ticker) + { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) + { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() + { + return this._ticker; + }, + }); + + /** + * Convenience method for stopping the render. + * @method PIXI.Application#stop + */ + this.stop = () => + { + this._ticker.stop(); + }; + + /** + * Convenience method for starting the render. + * @method PIXI.Application#start + */ + this.start = () => + { + this._ticker.start(); + }; + + /** + * Internal reference to the ticker + * @type {PIXI.Ticker} + * @name _ticker + * @memberof PIXI.Application# + * @private + */ + this._ticker = null; + + /** + * Ticker for doing render updates. + * @type {PIXI.Ticker} + * @name ticker + * @memberof PIXI.Application# + * @default PIXI.Ticker.shared + */ + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + + // Start the rendering + if (options.autoStart) + { + this.start(); + } + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + if (this._ticker) + { + const oldTicker = this._ticker; + + this.ticker = null; + oldTicker.destroy(); + } + } +} diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/packages/ticker/README.md b/packages/ticker/README.md index d7c459e..6c0f8e5 100644 --- a/packages/ticker/README.md +++ b/packages/ticker/README.md @@ -8,6 +8,23 @@ ## Usage +Create a Ticker object directly: + ```js -import * as ticker from '@pixi/ticker'; +import { Ticker } from '@pixi/ticker'; + +const ticker = new Ticker(); +ticker.start(); +``` + +Use as an Application plugin: + +```js +import { TickerPlugin } from '@pixi/ticker'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(TickerPlugin); + +const app = new Application(); +app.ticker.start(); ``` \ No newline at end of file diff --git a/packages/ticker/src/TickerPlugin.js b/packages/ticker/src/TickerPlugin.js new file mode 100644 index 0000000..834ac48 --- /dev/null +++ b/packages/ticker/src/TickerPlugin.js @@ -0,0 +1,108 @@ +import Ticker from './Ticker'; +import { UPDATE_PRIORITY } from './const'; + +/** + * Middleware for for Application Ticker. + * @example + * import {TickerPlugin} from '@pixi/ticker'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(TickerPlugin); + * @class + * @memberof PIXI + */ +export default class TickerPlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + // Set default + options = Object.assign({ + autoStart: true, + sharedTicker: false, + }, options); + + // Create ticker setter + Object.defineProperty(this, 'ticker', + { + set(ticker) + { + if (this._ticker) + { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) + { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() + { + return this._ticker; + }, + }); + + /** + * Convenience method for stopping the render. + * @method PIXI.Application#stop + */ + this.stop = () => + { + this._ticker.stop(); + }; + + /** + * Convenience method for starting the render. + * @method PIXI.Application#start + */ + this.start = () => + { + this._ticker.start(); + }; + + /** + * Internal reference to the ticker + * @type {PIXI.Ticker} + * @name _ticker + * @memberof PIXI.Application# + * @private + */ + this._ticker = null; + + /** + * Ticker for doing render updates. + * @type {PIXI.Ticker} + * @name ticker + * @memberof PIXI.Application# + * @default PIXI.Ticker.shared + */ + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + + // Start the rendering + if (options.autoStart) + { + this.start(); + } + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + if (this._ticker) + { + const oldTicker = this._ticker; + + this.ticker = null; + oldTicker.destroy(); + } + } +} diff --git a/packages/ticker/src/index.js b/packages/ticker/src/index.js index 19057ef..54fe3d7 100644 --- a/packages/ticker/src/index.js +++ b/packages/ticker/src/index.js @@ -1,4 +1,5 @@ export { default as Ticker } from './Ticker'; +export { default as TickerPlugin } from './TickerPlugin'; export * from './const'; import './settings'; diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/packages/ticker/README.md b/packages/ticker/README.md index d7c459e..6c0f8e5 100644 --- a/packages/ticker/README.md +++ b/packages/ticker/README.md @@ -8,6 +8,23 @@ ## Usage +Create a Ticker object directly: + ```js -import * as ticker from '@pixi/ticker'; +import { Ticker } from '@pixi/ticker'; + +const ticker = new Ticker(); +ticker.start(); +``` + +Use as an Application plugin: + +```js +import { TickerPlugin } from '@pixi/ticker'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(TickerPlugin); + +const app = new Application(); +app.ticker.start(); ``` \ No newline at end of file diff --git a/packages/ticker/src/TickerPlugin.js b/packages/ticker/src/TickerPlugin.js new file mode 100644 index 0000000..834ac48 --- /dev/null +++ b/packages/ticker/src/TickerPlugin.js @@ -0,0 +1,108 @@ +import Ticker from './Ticker'; +import { UPDATE_PRIORITY } from './const'; + +/** + * Middleware for for Application Ticker. + * @example + * import {TickerPlugin} from '@pixi/ticker'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(TickerPlugin); + * @class + * @memberof PIXI + */ +export default class TickerPlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + // Set default + options = Object.assign({ + autoStart: true, + sharedTicker: false, + }, options); + + // Create ticker setter + Object.defineProperty(this, 'ticker', + { + set(ticker) + { + if (this._ticker) + { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) + { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() + { + return this._ticker; + }, + }); + + /** + * Convenience method for stopping the render. + * @method PIXI.Application#stop + */ + this.stop = () => + { + this._ticker.stop(); + }; + + /** + * Convenience method for starting the render. + * @method PIXI.Application#start + */ + this.start = () => + { + this._ticker.start(); + }; + + /** + * Internal reference to the ticker + * @type {PIXI.Ticker} + * @name _ticker + * @memberof PIXI.Application# + * @private + */ + this._ticker = null; + + /** + * Ticker for doing render updates. + * @type {PIXI.Ticker} + * @name ticker + * @memberof PIXI.Application# + * @default PIXI.Ticker.shared + */ + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + + // Start the rendering + if (options.autoStart) + { + this.start(); + } + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + if (this._ticker) + { + const oldTicker = this._ticker; + + this.ticker = null; + oldTicker.destroy(); + } + } +} diff --git a/packages/ticker/src/index.js b/packages/ticker/src/index.js index 19057ef..54fe3d7 100644 --- a/packages/ticker/src/index.js +++ b/packages/ticker/src/index.js @@ -1,4 +1,5 @@ export { default as Ticker } from './Ticker'; +export { default as TickerPlugin } from './TickerPlugin'; export * from './const'; import './settings'; diff --git a/packages/ticker/test/Ticker.js b/packages/ticker/test/Ticker.js new file mode 100644 index 0000000..2e9eb16 --- /dev/null +++ b/packages/ticker/test/Ticker.js @@ -0,0 +1,405 @@ +const { Ticker, UPDATE_PRIORITY } = require('../'); +const { shared } = Ticker; + +describe('PIXI.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 () + { + const listener = sinon.spy(); + + shared.add(listener); // needed to autoStart + 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, UPDATE_PRIORITY.LOW) + .add(listener4, null, UPDATE_PRIORITY.INTERACTION) + .add(listener3, null, UPDATE_PRIORITY.HIGH) + .add(listener2, null, 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 when adding same priority', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1) + .add(listener2) + .add(listener3); + + shared.update(); + + expect(this.length()).to.equal(length + 3); + + sinon.assert.callOrder(listener1, listener2, listener3); + + shared.remove(listener1) + .remove(listener2) + .remove(listener3); + + expect(this.length()).to.equal(length); + }); + + it('should remove once listener in a stack', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1, null, UPDATE_PRIORITY.HIGH); + shared.addOnce(listener2, null, UPDATE_PRIORITY.NORMAL); + shared.add(listener3, null, UPDATE_PRIORITY.LOW); + + shared.update(); + + expect(this.length()).to.equal(length + 2); + + shared.update(); + + expect(listener1.calledTwice).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledTwice).to.be.true; + + shared.remove(listener1).remove(listener3); + + 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, UPDATE_PRIORITY.HIGH); + shared.add(lowListener, null, UPDATE_PRIORITY.LOW); + }); + + shared.add(mainListener, null, 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, UPDATE_PRIORITY.LOW); + }); + + shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); + shared.remove(listener1); + + // listener is removed right away + expect(this.length()).to.equal(length + 1); + }); + + shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); + + // listener is removed right away + expect(this.length()).to.equal(length + 1); + }); + + shared.add(listener1, null, UPDATE_PRIORITY.NORMAL); + + shared.update(); + + expect(this.length()).to.equal(length + 1); + + expect(listener2.called).to.be.false; + expect(listener1.calledOnce).to.be.true; + + shared.update(); + + 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, UPDATE_PRIORITY.HIGH); + shared.add(listener3, null, UPDATE_PRIORITY.LOW); + shared.add(listener4, null, 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, 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, UPDATE_PRIORITY.LOW); + ticker.start(); + }); + + it('should Ticker call destroyed listener "next" pointer after destroy', function (done) + { + const ticker = new Ticker(); + + const listener1 = sinon.spy(); + const listener2 = sinon.spy(() => + { + ticker.remove(listener2); + }); + + const listener3 = sinon.spy(() => + { + ticker.stop(); + + expect(listener1.calledOnce).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledOnce).to.be.true; + done(); + }); + + ticker.add(listener1, null, UPDATE_PRIORITY.HIGH); + ticker.add(listener2, null, UPDATE_PRIORITY.HIGH); + ticker.add(listener3, null, UPDATE_PRIORITY.HIGH); + + ticker.start(); + }); +}); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/packages/ticker/README.md b/packages/ticker/README.md index d7c459e..6c0f8e5 100644 --- a/packages/ticker/README.md +++ b/packages/ticker/README.md @@ -8,6 +8,23 @@ ## Usage +Create a Ticker object directly: + ```js -import * as ticker from '@pixi/ticker'; +import { Ticker } from '@pixi/ticker'; + +const ticker = new Ticker(); +ticker.start(); +``` + +Use as an Application plugin: + +```js +import { TickerPlugin } from '@pixi/ticker'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(TickerPlugin); + +const app = new Application(); +app.ticker.start(); ``` \ No newline at end of file diff --git a/packages/ticker/src/TickerPlugin.js b/packages/ticker/src/TickerPlugin.js new file mode 100644 index 0000000..834ac48 --- /dev/null +++ b/packages/ticker/src/TickerPlugin.js @@ -0,0 +1,108 @@ +import Ticker from './Ticker'; +import { UPDATE_PRIORITY } from './const'; + +/** + * Middleware for for Application Ticker. + * @example + * import {TickerPlugin} from '@pixi/ticker'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(TickerPlugin); + * @class + * @memberof PIXI + */ +export default class TickerPlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + // Set default + options = Object.assign({ + autoStart: true, + sharedTicker: false, + }, options); + + // Create ticker setter + Object.defineProperty(this, 'ticker', + { + set(ticker) + { + if (this._ticker) + { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) + { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() + { + return this._ticker; + }, + }); + + /** + * Convenience method for stopping the render. + * @method PIXI.Application#stop + */ + this.stop = () => + { + this._ticker.stop(); + }; + + /** + * Convenience method for starting the render. + * @method PIXI.Application#start + */ + this.start = () => + { + this._ticker.start(); + }; + + /** + * Internal reference to the ticker + * @type {PIXI.Ticker} + * @name _ticker + * @memberof PIXI.Application# + * @private + */ + this._ticker = null; + + /** + * Ticker for doing render updates. + * @type {PIXI.Ticker} + * @name ticker + * @memberof PIXI.Application# + * @default PIXI.Ticker.shared + */ + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + + // Start the rendering + if (options.autoStart) + { + this.start(); + } + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + if (this._ticker) + { + const oldTicker = this._ticker; + + this.ticker = null; + oldTicker.destroy(); + } + } +} diff --git a/packages/ticker/src/index.js b/packages/ticker/src/index.js index 19057ef..54fe3d7 100644 --- a/packages/ticker/src/index.js +++ b/packages/ticker/src/index.js @@ -1,4 +1,5 @@ export { default as Ticker } from './Ticker'; +export { default as TickerPlugin } from './TickerPlugin'; export * from './const'; import './settings'; diff --git a/packages/ticker/test/Ticker.js b/packages/ticker/test/Ticker.js new file mode 100644 index 0000000..2e9eb16 --- /dev/null +++ b/packages/ticker/test/Ticker.js @@ -0,0 +1,405 @@ +const { Ticker, UPDATE_PRIORITY } = require('../'); +const { shared } = Ticker; + +describe('PIXI.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 () + { + const listener = sinon.spy(); + + shared.add(listener); // needed to autoStart + 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, UPDATE_PRIORITY.LOW) + .add(listener4, null, UPDATE_PRIORITY.INTERACTION) + .add(listener3, null, UPDATE_PRIORITY.HIGH) + .add(listener2, null, 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 when adding same priority', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1) + .add(listener2) + .add(listener3); + + shared.update(); + + expect(this.length()).to.equal(length + 3); + + sinon.assert.callOrder(listener1, listener2, listener3); + + shared.remove(listener1) + .remove(listener2) + .remove(listener3); + + expect(this.length()).to.equal(length); + }); + + it('should remove once listener in a stack', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1, null, UPDATE_PRIORITY.HIGH); + shared.addOnce(listener2, null, UPDATE_PRIORITY.NORMAL); + shared.add(listener3, null, UPDATE_PRIORITY.LOW); + + shared.update(); + + expect(this.length()).to.equal(length + 2); + + shared.update(); + + expect(listener1.calledTwice).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledTwice).to.be.true; + + shared.remove(listener1).remove(listener3); + + 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, UPDATE_PRIORITY.HIGH); + shared.add(lowListener, null, UPDATE_PRIORITY.LOW); + }); + + shared.add(mainListener, null, 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, UPDATE_PRIORITY.LOW); + }); + + shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); + shared.remove(listener1); + + // listener is removed right away + expect(this.length()).to.equal(length + 1); + }); + + shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); + + // listener is removed right away + expect(this.length()).to.equal(length + 1); + }); + + shared.add(listener1, null, UPDATE_PRIORITY.NORMAL); + + shared.update(); + + expect(this.length()).to.equal(length + 1); + + expect(listener2.called).to.be.false; + expect(listener1.calledOnce).to.be.true; + + shared.update(); + + 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, UPDATE_PRIORITY.HIGH); + shared.add(listener3, null, UPDATE_PRIORITY.LOW); + shared.add(listener4, null, 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, 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, UPDATE_PRIORITY.LOW); + ticker.start(); + }); + + it('should Ticker call destroyed listener "next" pointer after destroy', function (done) + { + const ticker = new Ticker(); + + const listener1 = sinon.spy(); + const listener2 = sinon.spy(() => + { + ticker.remove(listener2); + }); + + const listener3 = sinon.spy(() => + { + ticker.stop(); + + expect(listener1.calledOnce).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledOnce).to.be.true; + done(); + }); + + ticker.add(listener1, null, UPDATE_PRIORITY.HIGH); + ticker.add(listener2, null, UPDATE_PRIORITY.HIGH); + ticker.add(listener3, null, UPDATE_PRIORITY.HIGH); + + ticker.start(); + }); +}); diff --git a/packages/ticker/test/TickerPlugin.js b/packages/ticker/test/TickerPlugin.js new file mode 100644 index 0000000..ab61e91 --- /dev/null +++ b/packages/ticker/test/TickerPlugin.js @@ -0,0 +1,90 @@ +const { TickerPlugin, UPDATE_PRIORITY, Ticker } = require('../'); + +describe('PIXI.TickerPlugin', function () +{ + it('should not start application before calling start method if options.autoStart is false', function (done) + { + const app = {}; + + TickerPlugin.init.call(app, { autoStart: false }); + + expect(app.ticker).to.be.instanceof(Ticker); + expect(app.ticker.started).to.be.false; + + app.start(); + + app.ticker.addOnce(() => + { + TickerPlugin.destroy.call(app); + done(); + }); + }); + + describe('set ticker', function () + { + before(function () + { + this.app = { + render() + { + // do nothing + }, + }; + TickerPlugin.init.call(this.app); + /* remove default listener to prevent uncaught exception */ + this.app._ticker.remove(this.app.render, this.app); + }); + + after(function () + { + TickerPlugin.destroy.call(this.app); + }); + + it('should assign ticker object', function () + { + const ticker = { add: sinon.spy() }; + const _ticker = { remove: sinon.spy() }; + + this.app._ticker = _ticker; + this.app.ticker = ticker; + + expect(_ticker.remove).to.be.calledOnce; + expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); + expect(_ticker.remove.args[0][1]).to.be.equal(this.app); + + expect(this.app._ticker).to.be.equal(ticker); + expect(ticker.add).to.be.calledOnce; + expect(ticker.add.args[0][0]).to.be.equal(this.app.render); + expect(ticker.add.args[0][1]).to.be.equal(this.app); + expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); + }); + + it('should assign ticker if no ticker', function () + { + const ticker = { add: sinon.spy() }; + + this.app._ticker = null; + this.app.ticker = ticker; + + expect(this.app._ticker).to.be.equal(ticker); + expect(ticker.add).to.be.calledOnce; + expect(ticker.add.args[0][0]).to.be.equal(this.app.render); + expect(ticker.add.args[0][1]).to.be.equal(this.app); + expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); + }); + + it('should assign null ticker', function () + { + const _ticker = { remove: sinon.spy() }; + + this.app._ticker = _ticker; + this.app.ticker = null; + + expect(_ticker.remove).to.be.calledOnce; + expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); + expect(_ticker.remove.args[0][1]).to.be.equal(this.app); + + expect(this.app._ticker).to.be.null; + }); + }); +}); diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json index 1c4fd50..49c7276 100644 --- a/bundles/pixi.js/package.json +++ b/bundles/pixi.js/package.json @@ -49,7 +49,6 @@ "@pixi/loaders": "^5.0.0-alpha.2", "@pixi/math": "^5.0.0-alpha.2", "@pixi/mesh": "^5.0.0-alpha.2", - "@pixi/mixin-app-loader": "^5.0.0-alpha.2", "@pixi/mixin-cache-as-bitmap": "^5.0.0-alpha.2", "@pixi/mixin-get-child-by-name": "^5.0.0-alpha.2", "@pixi/mixin-get-global-position": "^5.0.0-alpha.2", diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js index 5d3539d..a3a0d1b 100644 --- a/bundles/pixi.js/src/index.js +++ b/bundles/pixi.js/src/index.js @@ -26,7 +26,6 @@ import { DisplacementFilter } from '@pixi/filter-displacement'; import { FXAAFilter } from '@pixi/filter-fxaa'; import { NoiseFilter } from '@pixi/filter-noise'; -import '@pixi/mixin-app-loader'; import '@pixi/mixin-cache-as-bitmap'; import '@pixi/mixin-get-child-by-name'; import '@pixi/mixin-get-global-position'; @@ -46,6 +45,9 @@ loaders.Loader.registerPlugin(textBitmap.BitmapFontLoader); loaders.Loader.registerPlugin(spritesheet.SpritesheetLoader); +app.Application.registerPlugin(ticker.TickerPlugin); +app.Application.registerPlugin(loaders.LoaderPlugin); + // Apply deplayed mixins utils.mixins.performMixins(); diff --git a/packages/app/README.md b/packages/app/README.md index 5e0f14b..e3a8e9d 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -9,5 +9,15 @@ ## Usage ```js -import * as app from '@pixi/app'; -``` \ No newline at end of file +import { Application } from '@pixi/app'; + +const app = new Application(); +document.body.appendChild(app.view); +``` + +### Plugins + +PixiJS provides a few plugins to add features to the Application. These can be installed from the following packages. Use `Application.registerPlugin` to use these plugins. _Note: if you are using pixi.js or pixi.js-legacy bundles, this is unnecessary since plugins are installed automatically by default._ + +* **LoaderPlugin** from `@pixi/loaders` +* **TickerPlugin** from `@pixi/ticker` \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index a382cc2..a1fa6e6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,8 +27,7 @@ "dependencies": { "@pixi/core": "^5.0.0-alpha.2", "@pixi/display": "^5.0.0-alpha.2", - "@pixi/settings": "^5.0.0-alpha.2", - "@pixi/ticker": "^5.0.0-alpha.2" + "@pixi/settings": "^5.0.0-alpha.2" }, "devDependencies": { "@pixi/canvas-renderer": "^5.0.0-alpha.2", diff --git a/packages/app/src/Application.js b/packages/app/src/Application.js index 7856bea..e28117e 100644 --- a/packages/app/src/Application.js +++ b/packages/app/src/Application.js @@ -1,7 +1,6 @@ import { settings } from '@pixi/settings'; import { Container } from '@pixi/display'; import { Renderer } from '@pixi/core'; -import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker'; /** * Convenience class to create a new PIXI application. @@ -67,17 +66,9 @@ }, arg3); } - /** - * The default options, so we mixin functionality later. - * @member {object} - * @protected - */ - this._options = options = Object.assign({ - autoStart: true, - sharedTicker: false, + // The default options + options = Object.assign({ forceCanvas: false, - sharedLoader: false, - resizeTo: null, }, options); /** @@ -92,29 +83,21 @@ */ this.stage = new Container(); - /** - * Internal reference to the ticker - * @member {PIXI.Ticker} - * @private - */ - this._ticker = null; - - /** - * Ticker for doing render updates. - * @member {PIXI.Ticker} - * @default PIXI.Ticker.shared - */ - this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); - - // Resize - this.resize = this.resize.bind(this); - this.resizeTo = options.resizeTo; - - // Start the rendering - if (options.autoStart) + // install plugins here + Application._plugins.forEach((plugin) => { - this.start(); - } + plugin.init.call(this, options); + }); + } + + /** + * Register a middleware plugin for the application + * @static + * @param {PIXI.Application~Plugin} plugin - Plugin being installed + */ + static registerPlugin(plugin) + { + Application._plugins.push(plugin); } /** @@ -128,69 +111,6 @@ return new Renderer(options); } - set ticker(ticker) // eslint-disable-line require-jsdoc - { - if (this._ticker) - { - this._ticker.remove(this.render, this); - } - this._ticker = ticker; - if (ticker) - { - ticker.add(this.render, this, UPDATE_PRIORITY.LOW); - } - } - get ticker() // eslint-disable-line require-jsdoc - { - return this._ticker; - } - - /** - * The element or window to resize the application to. - * @member {Window|HTMLElement} - */ - set resizeTo(node) - { - window.removeEventListener('resize', this.resize); - this._resizeTo = node; - if (node) - { - window.addEventListener('resize', this.resize); - this.resize(); - } - } - get resizeTo() - { - return this._resizeTo; - } - - /** - * If `resizeTo` is set, calling this function - * will resize to the width and height of that element. - */ - resize() - { - if (this._resizeTo) - { - // Resize to the window - if (this._resizeTo === window) - { - this.renderer.resize( - window.innerWidth, - window.innerHeight - ); - } - // Resize to other HTML entities - else - { - this.renderer.resize( - this._resizeTo.clientWidth, - this._resizeTo.clientHeight - ); - } - } - } - /** * Render the current stage. */ @@ -200,22 +120,6 @@ } /** - * Convenience method for stopping the render. - */ - stop() - { - this._ticker.stop(); - } - - /** - * Convenience method for starting the render. - */ - start() - { - this._ticker.start(); - } - - /** * Reference to the renderer's canvas element. * @member {HTMLCanvasElement} * @readonly @@ -247,19 +151,19 @@ * @param {boolean} [stageOptions.baseTexture=false] - Only used for child Sprites if stageOptions.children is set * to true. Should it destroy the base texture of the child sprite */ - destroy(removeView, stageOptions) + destroy(removeView) { - this.resizeTo = null; + // Destroy plugins in the opposite order + // which they were constructed + const plugins = Application._plugins.slice(0); - if (this._ticker) + plugins.reverse(); + plugins.forEach((plugin) => { - const oldTicker = this._ticker; + plugin.destroy.call(this); + }); - this.ticker = null; - oldTicker.destroy(); - } - - this.stage.destroy(stageOptions); + this.stage.destroy(); this.stage = null; this.renderer.destroy(removeView); @@ -268,3 +172,18 @@ this._options = null; } } + +/** + * @typedef {object} PIXI.Application~Plugin + * @property {function} init - Called when Application is constructed, scoped to Application instance. + * Passes in `options` as the only argument, which are Application constructor options. + * @property {function} destroy - Called when destroying Application, scoped to Application instance + */ + +/** + * Collection of installed plugins. + * @static + * @private + * @type {PIXI.Application~Plugin[]} + */ +Application._plugins = []; diff --git a/packages/app/src/ResizePlugin.js b/packages/app/src/ResizePlugin.js new file mode 100644 index 0000000..80ac5d4 --- /dev/null +++ b/packages/app/src/ResizePlugin.js @@ -0,0 +1,83 @@ +/** + * Middleware for for Application's resize functionality + * @private + * @class + */ +export default class ResizePlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + /** + * The element or window to resize the application to. + * @type {Window|HTMLElement} + * @name resizeTo + * @memberof PIXI.Application# + */ + Object.defineProperty(this, 'resizeTo', + { + set(dom) + { + window.removeEventListener('resize', this.resize); + this._resizeTo = dom; + if (dom) + { + window.addEventListener('resize', this.resize); + this.resize(); + } + }, + get() + { + return this._resizeTo; + }, + }); + + /** + * If `resizeTo` is set, calling this function + * will resize to the width and height of that element. + * @method PIXI.Application#resize + */ + this.resize = () => + { + if (this._resizeTo) + { + // Resize to the window + if (this._resizeTo === window) + { + this.renderer.resize( + window.innerWidth, + window.innerHeight + ); + } + // Resize to other HTML entities + else + { + this.renderer.resize( + this._resizeTo.clientWidth, + this._resizeTo.clientHeight + ); + } + } + }; + + // On resize + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + this.resizeTo = null; + this.resize = null; + } +} diff --git a/packages/app/src/index.js b/packages/app/src/index.js index c5f6e6e..e6ea3b0 100644 --- a/packages/app/src/index.js +++ b/packages/app/src/index.js @@ -1 +1,6 @@ -export { default as Application } from './Application'; +import Application from './Application'; +import ResizePlugin from './ResizePlugin'; + +Application.registerPlugin(ResizePlugin); + +export { Application }; diff --git a/packages/app/test/index.js b/packages/app/test/index.js index 7b2aa40..1e84a1e 100644 --- a/packages/app/test/index.js +++ b/packages/app/test/index.js @@ -1,7 +1,6 @@ const { Application } = require('../'); const { autoDetectRenderer } = require('@pixi/canvas-renderer'); -const { Container, DisplayObject } = require('@pixi/display'); -const { Ticker, UPDATE_PRIORITY } = require('@pixi/ticker'); +const { Container } = require('@pixi/display'); const { skipHello } = require('@pixi/utils'); skipHello(); @@ -11,23 +10,40 @@ describe('PIXI.Application', function () { - it('should generate application', function (done) + it('should generate application', function () { expect(Application).to.be.a.function; const app = new Application(); expect(app.stage).to.be.instanceof(Container); - expect(app.ticker).to.be.instanceof(Ticker); expect(app.renderer).to.be.ok; - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); + app.destroy(); + + expect(app.stage).to.be.null; + expect(app.renderer).to.be.null; }); - it('should remove canvas when destroyed', function (done) + it('register a new plugin, then destroy it', function () + { + const plugin = { + init: sinon.spy(), + destroy: sinon.spy(), + }; + + Application.registerPlugin(plugin); + + const app = new Application(); + + app.destroy(); + + expect(plugin.init).to.be.calledOnce; + expect(plugin.destroy).to.be.calledOnce; + + Application._plugins.pop(); + }); + + it('should remove canvas when destroyed', function () { const app = new Application(); const view = app.view; @@ -35,89 +51,9 @@ expect(view).to.be.instanceof(HTMLCanvasElement); document.body.appendChild(view); - app.ticker.addOnce(() => - { - expect(document.body.contains(view)).to.be.true; - app.destroy(true); - expect(document.body.contains(view)).to.be.false; - done(); - }); - }); - - it('should not start application before calling start method if options.autoStart is false', function (done) - { - const app = new Application({ autoStart: false }); - - expect(app.ticker.started).to.be.false; - app.start(); - - app.ticker.addOnce(() => - { - app.destroy(); - done(); - }); - }); - - describe('set ticker', function () - { - before(function () - { - this.app = new Application(); - /* remove default listener to prevent uncaught exception */ - this.app._ticker.remove(this.app.render, this.app); - }); - - after(function () - { - this.app.destroy(true); - }); - - it('should assign ticker object', function () - { - const ticker = { add: sinon.spy() }; - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = ticker; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign ticker if no ticker', function () - { - const ticker = { add: sinon.spy() }; - - this.app._ticker = null; - this.app.ticker = ticker; - - expect(this.app._ticker).to.be.equal(ticker); - expect(ticker.add).to.be.calledOnce; - expect(ticker.add.args[0][0]).to.be.equal(this.app.render); - expect(ticker.add.args[0][1]).to.be.equal(this.app); - expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); - }); - - it('should assign null ticker', function () - { - const _ticker = { remove: sinon.spy() }; - - this.app._ticker = _ticker; - this.app.ticker = null; - - expect(_ticker.remove).to.be.calledOnce; - expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); - expect(_ticker.remove.args[0][1]).to.be.equal(this.app); - - expect(this.app._ticker).to.be.null; - }); + expect(document.body.contains(view)).to.be.true; + app.destroy(true); + expect(document.body.contains(view)).to.be.false; }); describe('resizeTo', function () @@ -177,44 +113,4 @@ app.destroy(); }); }); - - describe('destroy', function () - { - it('should not destroy children by default', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(); - expect(stage.children.length).to.be.equals(0); - expect(child.transform).to.not.be.null; - - done(); - }); - }); - - it('should allow children destroy', function (done) - { - const app = new Application(); - const stage = app.stage; - const child = new DisplayObject(); - - stage.addChild(child); - - app.ticker.addOnce(() => - { - app.destroy(false, true); - expect(stage.children.length).to.be.equals(0); - expect(stage.transform).to.be.null; - expect(child.transform).to.be.null; - - done(); - }); - }); - }); }); diff --git a/packages/canvas/canvas-graphics/src/Graphics.js b/packages/canvas/canvas-graphics/src/Graphics.js index 7a88896..2980573 100644 --- a/packages/canvas/canvas-graphics/src/Graphics.js +++ b/packages/canvas/canvas-graphics/src/Graphics.js @@ -35,7 +35,9 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); + const texture = Texture.from(canvasBuffer.baseTexture._canvasRenderTarget.canvas, { + scaleMode, + }); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/packages/loaders/README.md b/packages/loaders/README.md index 6e1030f..4046a02 100644 --- a/packages/loaders/README.md +++ b/packages/loaders/README.md @@ -8,6 +8,25 @@ ## Usage +Using the loader directly: + ```js -import '@pixi/loaders'; +import { Loader } from '@pixi/loaders'; + +const loader = new Loader(); +loader.add('path/to/file.jpg'); +loader.load(() => {}); +``` + +Using the loader as an Application plugin: + +```js +import { LoaderPlugin } from '@pixi/loaders'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(LoaderPlugin); + +const app = new Application(); +app.loader.add('path/to/file.jpg'); +app.loader.load(() => {}); ``` \ No newline at end of file diff --git a/packages/loaders/src/LoaderPlugin.js b/packages/loaders/src/LoaderPlugin.js new file mode 100644 index 0000000..c586e58 --- /dev/null +++ b/packages/loaders/src/LoaderPlugin.js @@ -0,0 +1,47 @@ +import Loader from './Loader'; + +/** + * Application plugin for supporting loader option. Installing the LoaderPlugin + * is not necessary if using **pixi.js** or **pixi.js-legacy**. + * @example + * import {LoaderPlugin} from '@pixi/loaders'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(LoaderPlugin); + * @class + * @memberof PIXI + */ +export default class LoaderPlugin +{ + /** + * Called on application constructor + * @param {object} options + * @private + */ + static init(options) + { + options = Object.assign({ + sharedLoader: false, + }, options); + + /** + * Loader instance to help with asset loading. + * @name PIXI.Application#loader + * @type {PIXI.Loader} + * @readonly + */ + this.loader = options.sharedLoader ? Loader.shared : new Loader(); + } + + /** + * Called when application destroyed + * @private + */ + static destroy() + { + if (this.loader) + { + this.loader.destroy(); + this.loader = null; + } + } +} diff --git a/packages/loaders/src/index.js b/packages/loaders/src/index.js index 3e877fe..10aa771 100644 --- a/packages/loaders/src/index.js +++ b/packages/loaders/src/index.js @@ -9,3 +9,4 @@ export const LoaderResource = Resource; export { default as Loader } from './Loader'; export { default as TextureLoader } from './TextureLoader'; +export { default as LoaderPlugin } from './LoaderPlugin'; diff --git a/packages/loaders/test/LoaderPlugin.js b/packages/loaders/test/LoaderPlugin.js new file mode 100644 index 0000000..84ed306 --- /dev/null +++ b/packages/loaders/test/LoaderPlugin.js @@ -0,0 +1,33 @@ +const { LoaderPlugin, Loader } = require('../'); + +describe('PIXI.LoaderPlugin', function () +{ + it('should contain loader property', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); + + it('should use sharedLoader option', function () + { + const obj = {}; + + LoaderPlugin.init.call(obj, { sharedLoader: true }); + + expect(obj.loader).to.be.not.undefined; + expect(obj.loader).to.be.instanceof(Loader); + expect(obj.loader).to.equal(Loader.shared); + + LoaderPlugin.destroy.call(obj); + + expect(obj.loader).to.be.null; + }); +}); diff --git a/packages/loaders/test/index.js b/packages/loaders/test/index.js index 8913167..629fbb7 100644 --- a/packages/loaders/test/index.js +++ b/packages/loaders/test/index.js @@ -1,2 +1,3 @@ require('./Loader'); require('./TextureLoader'); +require('./LoaderPlugin'); diff --git a/packages/mixin-app-loader/LICENSE b/packages/mixin-app-loader/LICENSE deleted file mode 100644 index 148e3eb..0000000 --- a/packages/mixin-app-loader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2013-2018 Mathew Groves, Chad Engler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/mixin-app-loader/README.md b/packages/mixin-app-loader/README.md deleted file mode 100644 index 3e89f86..0000000 --- a/packages/mixin-app-loader/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# @pixi/mixin-app-loader - -## Installation - -```bash -npm install @pixi/mixin-app-loader -``` - -## Usage - -```js -import '@pixi/mixin-app-loader'; -``` \ No newline at end of file diff --git a/packages/mixin-app-loader/package.json b/packages/mixin-app-loader/package.json deleted file mode 100644 index 9c23c20..0000000 --- a/packages/mixin-app-loader/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@pixi/mixin-app-loader", - "version": "5.0.0-alpha.2", - "main": "lib/mixin-app-loader.js", - "module": "lib/mixin-app-loader.es.js", - "description": "Support for loader in Application", - "author": "Mat Groves", - "contributors": [ - "Matt Karl " - ], - "homepage": "http://pixijs.com/", - "bugs": "https://github.com/pixijs/pixi.js/issues", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pixijs/pixi.js.git" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "test": "floss --path test" - }, - "files": [ - "lib" - ], - "dependencies": { - "@pixi/app": "^5.0.0-alpha.2", - "@pixi/loaders": "^5.0.0-alpha.2" - }, - "devDependencies": { - "@pixi/canvas-renderer": "^5.0.0-alpha.2", - "@pixi/utils": "^5.0.0-alpha.2", - "floss": "^2.1.3" - } -} diff --git a/packages/mixin-app-loader/src/index.js b/packages/mixin-app-loader/src/index.js deleted file mode 100644 index 5fa0f4f..0000000 --- a/packages/mixin-app-loader/src/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Application } from '@pixi/app'; -import { Loader } from '@pixi/loaders'; - -Application.prototype._loader = null; - -/** - * Loader instance to help with asset loading. - * @name PIXI.Application#loader - * @type {PIXI.Loader} - */ -Object.defineProperties(Application.prototype, { - loader: { - get() - { - if (!this._loader && this._options) - { - const { sharedLoader } = this._options; - - this._loader = sharedLoader ? Loader.shared : new Loader(); - } - - return this._loader; - }, - }, -}); - -// Override the destroy function -// making sure to destroy the current Loader -Application.prototype._parentDestroy = Application.prototype.destroy; -Application.prototype.destroy = function destroy(removeView, stageOptions) -{ - if (this._loader) - { - this._loader.destroy(); - this._loader = null; - } - this._parentDestroy(removeView, stageOptions); -}; diff --git a/packages/mixin-app-loader/test/.eslintrc.json b/packages/mixin-app-loader/test/.eslintrc.json deleted file mode 100644 index 2094b04..0000000 --- a/packages/mixin-app-loader/test/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "expect": false, - "assert": false, - "sinon": false, - "PIXI": false - }, - "rules": { - "func-names": 0, - "no-unused-expressions": 0 - } -} \ No newline at end of file diff --git a/packages/mixin-app-loader/test/index.js b/packages/mixin-app-loader/test/index.js deleted file mode 100644 index 7eb948b..0000000 --- a/packages/mixin-app-loader/test/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Application } = require('@pixi/app'); -const { Loader } = require('@pixi/loaders'); -const { skipHello } = require('@pixi/utils'); -const { autoDetectRenderer } = require('@pixi/canvas-renderer'); - -require('../'); - -skipHello(); - -// Use fallback if no webgl -Application.prototype.createRenderer = autoDetectRenderer; - -describe('PIXI.Application#loader', function () -{ - it('should contain loader property', function () - { - const obj = new Application(); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); - - it('should use sharedLoader option', function () - { - const obj = new Application({ sharedLoader: true }); - - expect(obj.loader).to.be.not.undefined; - expect(obj.loader).to.be.instanceof(Loader); - expect(obj.loader).to.equal(Loader.shared); - - obj.destroy(); - - expect(obj.loader).to.be.null; - }); -}); diff --git a/packages/ticker/README.md b/packages/ticker/README.md index d7c459e..6c0f8e5 100644 --- a/packages/ticker/README.md +++ b/packages/ticker/README.md @@ -8,6 +8,23 @@ ## Usage +Create a Ticker object directly: + ```js -import * as ticker from '@pixi/ticker'; +import { Ticker } from '@pixi/ticker'; + +const ticker = new Ticker(); +ticker.start(); +``` + +Use as an Application plugin: + +```js +import { TickerPlugin } from '@pixi/ticker'; +import { Application } from '@pixi/app'; + +Application.registerPlugin(TickerPlugin); + +const app = new Application(); +app.ticker.start(); ``` \ No newline at end of file diff --git a/packages/ticker/src/TickerPlugin.js b/packages/ticker/src/TickerPlugin.js new file mode 100644 index 0000000..834ac48 --- /dev/null +++ b/packages/ticker/src/TickerPlugin.js @@ -0,0 +1,108 @@ +import Ticker from './Ticker'; +import { UPDATE_PRIORITY } from './const'; + +/** + * Middleware for for Application Ticker. + * @example + * import {TickerPlugin} from '@pixi/ticker'; + * import {Application} from '@pixi/app'; + * Application.registerPlugin(TickerPlugin); + * @class + * @memberof PIXI + */ +export default class TickerPlugin +{ + /** + * Initialize the plugin with scope of application instance + * @static + * @private + * @param {object} [options] - See application options + */ + static init(options) + { + // Set default + options = Object.assign({ + autoStart: true, + sharedTicker: false, + }, options); + + // Create ticker setter + Object.defineProperty(this, 'ticker', + { + set(ticker) + { + if (this._ticker) + { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) + { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() + { + return this._ticker; + }, + }); + + /** + * Convenience method for stopping the render. + * @method PIXI.Application#stop + */ + this.stop = () => + { + this._ticker.stop(); + }; + + /** + * Convenience method for starting the render. + * @method PIXI.Application#start + */ + this.start = () => + { + this._ticker.start(); + }; + + /** + * Internal reference to the ticker + * @type {PIXI.Ticker} + * @name _ticker + * @memberof PIXI.Application# + * @private + */ + this._ticker = null; + + /** + * Ticker for doing render updates. + * @type {PIXI.Ticker} + * @name ticker + * @memberof PIXI.Application# + * @default PIXI.Ticker.shared + */ + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + + // Start the rendering + if (options.autoStart) + { + this.start(); + } + } + + /** + * Clean up the ticker, scoped to application + * @static + * @private + */ + static destroy() + { + if (this._ticker) + { + const oldTicker = this._ticker; + + this.ticker = null; + oldTicker.destroy(); + } + } +} diff --git a/packages/ticker/src/index.js b/packages/ticker/src/index.js index 19057ef..54fe3d7 100644 --- a/packages/ticker/src/index.js +++ b/packages/ticker/src/index.js @@ -1,4 +1,5 @@ export { default as Ticker } from './Ticker'; +export { default as TickerPlugin } from './TickerPlugin'; export * from './const'; import './settings'; diff --git a/packages/ticker/test/Ticker.js b/packages/ticker/test/Ticker.js new file mode 100644 index 0000000..2e9eb16 --- /dev/null +++ b/packages/ticker/test/Ticker.js @@ -0,0 +1,405 @@ +const { Ticker, UPDATE_PRIORITY } = require('../'); +const { shared } = Ticker; + +describe('PIXI.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 () + { + const listener = sinon.spy(); + + shared.add(listener); // needed to autoStart + 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, UPDATE_PRIORITY.LOW) + .add(listener4, null, UPDATE_PRIORITY.INTERACTION) + .add(listener3, null, UPDATE_PRIORITY.HIGH) + .add(listener2, null, 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 when adding same priority', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1) + .add(listener2) + .add(listener3); + + shared.update(); + + expect(this.length()).to.equal(length + 3); + + sinon.assert.callOrder(listener1, listener2, listener3); + + shared.remove(listener1) + .remove(listener2) + .remove(listener3); + + expect(this.length()).to.equal(length); + }); + + it('should remove once listener in a stack', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1, null, UPDATE_PRIORITY.HIGH); + shared.addOnce(listener2, null, UPDATE_PRIORITY.NORMAL); + shared.add(listener3, null, UPDATE_PRIORITY.LOW); + + shared.update(); + + expect(this.length()).to.equal(length + 2); + + shared.update(); + + expect(listener1.calledTwice).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledTwice).to.be.true; + + shared.remove(listener1).remove(listener3); + + 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, UPDATE_PRIORITY.HIGH); + shared.add(lowListener, null, UPDATE_PRIORITY.LOW); + }); + + shared.add(mainListener, null, 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, UPDATE_PRIORITY.LOW); + }); + + shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); + shared.remove(listener1); + + // listener is removed right away + expect(this.length()).to.equal(length + 1); + }); + + shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); + + // listener is removed right away + expect(this.length()).to.equal(length + 1); + }); + + shared.add(listener1, null, UPDATE_PRIORITY.NORMAL); + + shared.update(); + + expect(this.length()).to.equal(length + 1); + + expect(listener2.called).to.be.false; + expect(listener1.calledOnce).to.be.true; + + shared.update(); + + 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, UPDATE_PRIORITY.HIGH); + shared.add(listener3, null, UPDATE_PRIORITY.LOW); + shared.add(listener4, null, 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, 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, UPDATE_PRIORITY.LOW); + ticker.start(); + }); + + it('should Ticker call destroyed listener "next" pointer after destroy', function (done) + { + const ticker = new Ticker(); + + const listener1 = sinon.spy(); + const listener2 = sinon.spy(() => + { + ticker.remove(listener2); + }); + + const listener3 = sinon.spy(() => + { + ticker.stop(); + + expect(listener1.calledOnce).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledOnce).to.be.true; + done(); + }); + + ticker.add(listener1, null, UPDATE_PRIORITY.HIGH); + ticker.add(listener2, null, UPDATE_PRIORITY.HIGH); + ticker.add(listener3, null, UPDATE_PRIORITY.HIGH); + + ticker.start(); + }); +}); diff --git a/packages/ticker/test/TickerPlugin.js b/packages/ticker/test/TickerPlugin.js new file mode 100644 index 0000000..ab61e91 --- /dev/null +++ b/packages/ticker/test/TickerPlugin.js @@ -0,0 +1,90 @@ +const { TickerPlugin, UPDATE_PRIORITY, Ticker } = require('../'); + +describe('PIXI.TickerPlugin', function () +{ + it('should not start application before calling start method if options.autoStart is false', function (done) + { + const app = {}; + + TickerPlugin.init.call(app, { autoStart: false }); + + expect(app.ticker).to.be.instanceof(Ticker); + expect(app.ticker.started).to.be.false; + + app.start(); + + app.ticker.addOnce(() => + { + TickerPlugin.destroy.call(app); + done(); + }); + }); + + describe('set ticker', function () + { + before(function () + { + this.app = { + render() + { + // do nothing + }, + }; + TickerPlugin.init.call(this.app); + /* remove default listener to prevent uncaught exception */ + this.app._ticker.remove(this.app.render, this.app); + }); + + after(function () + { + TickerPlugin.destroy.call(this.app); + }); + + it('should assign ticker object', function () + { + const ticker = { add: sinon.spy() }; + const _ticker = { remove: sinon.spy() }; + + this.app._ticker = _ticker; + this.app.ticker = ticker; + + expect(_ticker.remove).to.be.calledOnce; + expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); + expect(_ticker.remove.args[0][1]).to.be.equal(this.app); + + expect(this.app._ticker).to.be.equal(ticker); + expect(ticker.add).to.be.calledOnce; + expect(ticker.add.args[0][0]).to.be.equal(this.app.render); + expect(ticker.add.args[0][1]).to.be.equal(this.app); + expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); + }); + + it('should assign ticker if no ticker', function () + { + const ticker = { add: sinon.spy() }; + + this.app._ticker = null; + this.app.ticker = ticker; + + expect(this.app._ticker).to.be.equal(ticker); + expect(ticker.add).to.be.calledOnce; + expect(ticker.add.args[0][0]).to.be.equal(this.app.render); + expect(ticker.add.args[0][1]).to.be.equal(this.app); + expect(ticker.add.args[0][2]).to.be.equal(UPDATE_PRIORITY.LOW); + }); + + it('should assign null ticker', function () + { + const _ticker = { remove: sinon.spy() }; + + this.app._ticker = _ticker; + this.app.ticker = null; + + expect(_ticker.remove).to.be.calledOnce; + expect(_ticker.remove.args[0][0]).to.be.equal(this.app.render); + expect(_ticker.remove.args[0][1]).to.be.equal(this.app); + + expect(this.app._ticker).to.be.null; + }); + }); +}); diff --git a/packages/ticker/test/index.js b/packages/ticker/test/index.js index 2e9eb16..02d6526 100644 --- a/packages/ticker/test/index.js +++ b/packages/ticker/test/index.js @@ -1,405 +1,2 @@ -const { Ticker, UPDATE_PRIORITY } = require('../'); -const { shared } = Ticker; - -describe('PIXI.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 () - { - const listener = sinon.spy(); - - shared.add(listener); // needed to autoStart - 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, UPDATE_PRIORITY.LOW) - .add(listener4, null, UPDATE_PRIORITY.INTERACTION) - .add(listener3, null, UPDATE_PRIORITY.HIGH) - .add(listener2, null, 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 when adding same priority', function () - { - const length = this.length(); - const listener1 = sinon.spy(); - const listener2 = sinon.spy(); - const listener3 = sinon.spy(); - - shared.add(listener1) - .add(listener2) - .add(listener3); - - shared.update(); - - expect(this.length()).to.equal(length + 3); - - sinon.assert.callOrder(listener1, listener2, listener3); - - shared.remove(listener1) - .remove(listener2) - .remove(listener3); - - expect(this.length()).to.equal(length); - }); - - it('should remove once listener in a stack', function () - { - const length = this.length(); - const listener1 = sinon.spy(); - const listener2 = sinon.spy(); - const listener3 = sinon.spy(); - - shared.add(listener1, null, UPDATE_PRIORITY.HIGH); - shared.addOnce(listener2, null, UPDATE_PRIORITY.NORMAL); - shared.add(listener3, null, UPDATE_PRIORITY.LOW); - - shared.update(); - - expect(this.length()).to.equal(length + 2); - - shared.update(); - - expect(listener1.calledTwice).to.be.true; - expect(listener2.calledOnce).to.be.true; - expect(listener3.calledTwice).to.be.true; - - shared.remove(listener1).remove(listener3); - - 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, UPDATE_PRIORITY.HIGH); - shared.add(lowListener, null, UPDATE_PRIORITY.LOW); - }); - - shared.add(mainListener, null, 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, UPDATE_PRIORITY.LOW); - }); - - shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); - shared.remove(listener1); - - // listener is removed right away - expect(this.length()).to.equal(length + 1); - }); - - shared.add(listener1, null, 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, UPDATE_PRIORITY.LOW); - - // listener is removed right away - expect(this.length()).to.equal(length + 1); - }); - - shared.add(listener1, null, UPDATE_PRIORITY.NORMAL); - - shared.update(); - - expect(this.length()).to.equal(length + 1); - - expect(listener2.called).to.be.false; - expect(listener1.calledOnce).to.be.true; - - shared.update(); - - 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, UPDATE_PRIORITY.HIGH); - shared.add(listener3, null, UPDATE_PRIORITY.LOW); - shared.add(listener4, null, 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, 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, UPDATE_PRIORITY.LOW); - ticker.start(); - }); - - it('should Ticker call destroyed listener "next" pointer after destroy', function (done) - { - const ticker = new Ticker(); - - const listener1 = sinon.spy(); - const listener2 = sinon.spy(() => - { - ticker.remove(listener2); - }); - - const listener3 = sinon.spy(() => - { - ticker.stop(); - - expect(listener1.calledOnce).to.be.true; - expect(listener2.calledOnce).to.be.true; - expect(listener3.calledOnce).to.be.true; - done(); - }); - - ticker.add(listener1, null, UPDATE_PRIORITY.HIGH); - ticker.add(listener2, null, UPDATE_PRIORITY.HIGH); - ticker.add(listener3, null, UPDATE_PRIORITY.HIGH); - - ticker.start(); - }); -}); +require('./Ticker'); +require('./TickerPlugin');