diff --git a/packages/display/package.json b/packages/display/package.json index ccb7bf2..c187fac 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -26,6 +26,7 @@ ], "dependencies": { "@pixi/math": "^5.0.0-alpha.3", + "@pixi/settings": "^5.0.0-alpha.3", "@pixi/utils": "^5.0.0-alpha.3", "eventemitter3": "^2.0.0", "remove-array-items": "^1.0.0" diff --git a/packages/display/package.json b/packages/display/package.json index ccb7bf2..c187fac 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -26,6 +26,7 @@ ], "dependencies": { "@pixi/math": "^5.0.0-alpha.3", + "@pixi/settings": "^5.0.0-alpha.3", "@pixi/utils": "^5.0.0-alpha.3", "eventemitter3": "^2.0.0", "remove-array-items": "^1.0.0" diff --git a/packages/display/src/Container.js b/packages/display/src/Container.js index 08dd670..119eb35 100644 --- a/packages/display/src/Container.js +++ b/packages/display/src/Container.js @@ -1,6 +1,17 @@ +import { settings } from '@pixi/settings'; import removeItems from 'remove-array-items'; import DisplayObject from './DisplayObject'; +function sortChildren(a, b) +{ + if (a.zIndex === b.zIndex) + { + return a._lastSortedIndex - b._lastSortedIndex; + } + + return a.zIndex - b.zIndex; +} + /** * A Container represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. @@ -30,6 +41,31 @@ * @readonly */ this.children = []; + + /** + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @see PIXI.settings.SORTABLE_CHILDREN + * + * @member {boolean} + */ + this.sortableChildren = settings.SORTABLE_CHILDREN; + + /** + * Should children be sorted by zIndex at the next updateTransform call. + * Will get automatically set to true if a new child is added, or if a child's zIndex changes. + * + * @member {boolean} + */ + this.sortDirty = false; } /** @@ -73,6 +109,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -109,6 +147,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -314,10 +354,42 @@ } /** + * Sorts children by zIndex. Previous order is mantained for 2 children with the same zIndex. + */ + sortChildren() + { + let sortRequired = false; + + for (let i = 0, j = this.children.length; i < j; ++i) + { + const child = this.children[i]; + + child._lastSortedIndex = i; + + if (!sortRequired && child.zIndex !== 0) + { + sortRequired = true; + } + } + + if (sortRequired && this.children.length > 1) + { + this.children.sort(sortChildren); + } + + this.sortDirty = false; + } + + /** * Updates the transform on all children of this container for rendering */ updateTransform() { + if (this.sortableChildren && this.sortDirty) + { + this.sortChildren(); + } + this._boundsID++; this.transform.updateTransform(this.parent.transform); @@ -508,6 +580,8 @@ { super.destroy(); + this.sortDirty = false; + const destroyChildren = typeof options === 'boolean' ? options : options && options.children; const oldChildren = this.removeChildren(0, this.children.length); diff --git a/packages/display/package.json b/packages/display/package.json index ccb7bf2..c187fac 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -26,6 +26,7 @@ ], "dependencies": { "@pixi/math": "^5.0.0-alpha.3", + "@pixi/settings": "^5.0.0-alpha.3", "@pixi/utils": "^5.0.0-alpha.3", "eventemitter3": "^2.0.0", "remove-array-items": "^1.0.0" diff --git a/packages/display/src/Container.js b/packages/display/src/Container.js index 08dd670..119eb35 100644 --- a/packages/display/src/Container.js +++ b/packages/display/src/Container.js @@ -1,6 +1,17 @@ +import { settings } from '@pixi/settings'; import removeItems from 'remove-array-items'; import DisplayObject from './DisplayObject'; +function sortChildren(a, b) +{ + if (a.zIndex === b.zIndex) + { + return a._lastSortedIndex - b._lastSortedIndex; + } + + return a.zIndex - b.zIndex; +} + /** * A Container represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. @@ -30,6 +41,31 @@ * @readonly */ this.children = []; + + /** + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @see PIXI.settings.SORTABLE_CHILDREN + * + * @member {boolean} + */ + this.sortableChildren = settings.SORTABLE_CHILDREN; + + /** + * Should children be sorted by zIndex at the next updateTransform call. + * Will get automatically set to true if a new child is added, or if a child's zIndex changes. + * + * @member {boolean} + */ + this.sortDirty = false; } /** @@ -73,6 +109,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -109,6 +147,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -314,10 +354,42 @@ } /** + * Sorts children by zIndex. Previous order is mantained for 2 children with the same zIndex. + */ + sortChildren() + { + let sortRequired = false; + + for (let i = 0, j = this.children.length; i < j; ++i) + { + const child = this.children[i]; + + child._lastSortedIndex = i; + + if (!sortRequired && child.zIndex !== 0) + { + sortRequired = true; + } + } + + if (sortRequired && this.children.length > 1) + { + this.children.sort(sortChildren); + } + + this.sortDirty = false; + } + + /** * Updates the transform on all children of this container for rendering */ updateTransform() { + if (this.sortableChildren && this.sortDirty) + { + this.sortChildren(); + } + this._boundsID++; this.transform.updateTransform(this.parent.transform); @@ -508,6 +580,8 @@ { super.destroy(); + this.sortDirty = false; + const destroyChildren = typeof options === 'boolean' ? options : options && options.children; const oldChildren = this.removeChildren(0, this.children.length); diff --git a/packages/display/src/DisplayObject.js b/packages/display/src/DisplayObject.js index fdd5b09..ebcd232 100644 --- a/packages/display/src/DisplayObject.js +++ b/packages/display/src/DisplayObject.js @@ -75,6 +75,25 @@ this.worldAlpha = 1; /** + * Which index in the children array the display component was before the previous zIndex sort. + * Used by containers to help sort objects with the same zIndex, by using previous array index as the decider. + * + * @member {number} + * @private + * @readOnly + */ + this._lastSortedIndex = 0; + + /** + * The zIndex of the displayObject. + * A higher value will mean it will be rendered on top of other displayObjects within the same container. + * + * @member {number} + * @private + */ + this._zIndex = 0; + + /** * The area the filter is applied to. This is used as more of an optimization * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle. * @@ -571,6 +590,28 @@ } /** + * The zIndex of the displayObject. + * If a container has the sortableChildren property set to true, children will be automatically + * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array, + * and thus rendered on top of other displayObjects within the same container. + * + * @member {number} + */ + get zIndex() + { + return this._zIndex; + } + + set zIndex(value) // eslint-disable-line require-jsdoc + { + this._zIndex = value; + if (this.parent) + { + this.parent.sortDirty = true; + } + } + + /** * Indicates if the object is globally visible. * * @member {boolean} diff --git a/packages/display/package.json b/packages/display/package.json index ccb7bf2..c187fac 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -26,6 +26,7 @@ ], "dependencies": { "@pixi/math": "^5.0.0-alpha.3", + "@pixi/settings": "^5.0.0-alpha.3", "@pixi/utils": "^5.0.0-alpha.3", "eventemitter3": "^2.0.0", "remove-array-items": "^1.0.0" diff --git a/packages/display/src/Container.js b/packages/display/src/Container.js index 08dd670..119eb35 100644 --- a/packages/display/src/Container.js +++ b/packages/display/src/Container.js @@ -1,6 +1,17 @@ +import { settings } from '@pixi/settings'; import removeItems from 'remove-array-items'; import DisplayObject from './DisplayObject'; +function sortChildren(a, b) +{ + if (a.zIndex === b.zIndex) + { + return a._lastSortedIndex - b._lastSortedIndex; + } + + return a.zIndex - b.zIndex; +} + /** * A Container represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. @@ -30,6 +41,31 @@ * @readonly */ this.children = []; + + /** + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @see PIXI.settings.SORTABLE_CHILDREN + * + * @member {boolean} + */ + this.sortableChildren = settings.SORTABLE_CHILDREN; + + /** + * Should children be sorted by zIndex at the next updateTransform call. + * Will get automatically set to true if a new child is added, or if a child's zIndex changes. + * + * @member {boolean} + */ + this.sortDirty = false; } /** @@ -73,6 +109,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -109,6 +147,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -314,10 +354,42 @@ } /** + * Sorts children by zIndex. Previous order is mantained for 2 children with the same zIndex. + */ + sortChildren() + { + let sortRequired = false; + + for (let i = 0, j = this.children.length; i < j; ++i) + { + const child = this.children[i]; + + child._lastSortedIndex = i; + + if (!sortRequired && child.zIndex !== 0) + { + sortRequired = true; + } + } + + if (sortRequired && this.children.length > 1) + { + this.children.sort(sortChildren); + } + + this.sortDirty = false; + } + + /** * Updates the transform on all children of this container for rendering */ updateTransform() { + if (this.sortableChildren && this.sortDirty) + { + this.sortChildren(); + } + this._boundsID++; this.transform.updateTransform(this.parent.transform); @@ -508,6 +580,8 @@ { super.destroy(); + this.sortDirty = false; + const destroyChildren = typeof options === 'boolean' ? options : options && options.children; const oldChildren = this.removeChildren(0, this.children.length); diff --git a/packages/display/src/DisplayObject.js b/packages/display/src/DisplayObject.js index fdd5b09..ebcd232 100644 --- a/packages/display/src/DisplayObject.js +++ b/packages/display/src/DisplayObject.js @@ -75,6 +75,25 @@ this.worldAlpha = 1; /** + * Which index in the children array the display component was before the previous zIndex sort. + * Used by containers to help sort objects with the same zIndex, by using previous array index as the decider. + * + * @member {number} + * @private + * @readOnly + */ + this._lastSortedIndex = 0; + + /** + * The zIndex of the displayObject. + * A higher value will mean it will be rendered on top of other displayObjects within the same container. + * + * @member {number} + * @private + */ + this._zIndex = 0; + + /** * The area the filter is applied to. This is used as more of an optimization * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle. * @@ -571,6 +590,28 @@ } /** + * The zIndex of the displayObject. + * If a container has the sortableChildren property set to true, children will be automatically + * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array, + * and thus rendered on top of other displayObjects within the same container. + * + * @member {number} + */ + get zIndex() + { + return this._zIndex; + } + + set zIndex(value) // eslint-disable-line require-jsdoc + { + this._zIndex = value; + if (this.parent) + { + this.parent.sortDirty = true; + } + } + + /** * Indicates if the object is globally visible. * * @member {boolean} diff --git a/packages/display/src/index.js b/packages/display/src/index.js index 2d664ea..48acb62 100644 --- a/packages/display/src/index.js +++ b/packages/display/src/index.js @@ -1,3 +1,5 @@ +import './settings'; + export { default as Bounds } from './Bounds'; export { default as DisplayObject } from './DisplayObject'; export { default as Container } from './Container'; diff --git a/packages/display/package.json b/packages/display/package.json index ccb7bf2..c187fac 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -26,6 +26,7 @@ ], "dependencies": { "@pixi/math": "^5.0.0-alpha.3", + "@pixi/settings": "^5.0.0-alpha.3", "@pixi/utils": "^5.0.0-alpha.3", "eventemitter3": "^2.0.0", "remove-array-items": "^1.0.0" diff --git a/packages/display/src/Container.js b/packages/display/src/Container.js index 08dd670..119eb35 100644 --- a/packages/display/src/Container.js +++ b/packages/display/src/Container.js @@ -1,6 +1,17 @@ +import { settings } from '@pixi/settings'; import removeItems from 'remove-array-items'; import DisplayObject from './DisplayObject'; +function sortChildren(a, b) +{ + if (a.zIndex === b.zIndex) + { + return a._lastSortedIndex - b._lastSortedIndex; + } + + return a.zIndex - b.zIndex; +} + /** * A Container represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. @@ -30,6 +41,31 @@ * @readonly */ this.children = []; + + /** + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @see PIXI.settings.SORTABLE_CHILDREN + * + * @member {boolean} + */ + this.sortableChildren = settings.SORTABLE_CHILDREN; + + /** + * Should children be sorted by zIndex at the next updateTransform call. + * Will get automatically set to true if a new child is added, or if a child's zIndex changes. + * + * @member {boolean} + */ + this.sortDirty = false; } /** @@ -73,6 +109,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -109,6 +147,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -314,10 +354,42 @@ } /** + * Sorts children by zIndex. Previous order is mantained for 2 children with the same zIndex. + */ + sortChildren() + { + let sortRequired = false; + + for (let i = 0, j = this.children.length; i < j; ++i) + { + const child = this.children[i]; + + child._lastSortedIndex = i; + + if (!sortRequired && child.zIndex !== 0) + { + sortRequired = true; + } + } + + if (sortRequired && this.children.length > 1) + { + this.children.sort(sortChildren); + } + + this.sortDirty = false; + } + + /** * Updates the transform on all children of this container for rendering */ updateTransform() { + if (this.sortableChildren && this.sortDirty) + { + this.sortChildren(); + } + this._boundsID++; this.transform.updateTransform(this.parent.transform); @@ -508,6 +580,8 @@ { super.destroy(); + this.sortDirty = false; + const destroyChildren = typeof options === 'boolean' ? options : options && options.children; const oldChildren = this.removeChildren(0, this.children.length); diff --git a/packages/display/src/DisplayObject.js b/packages/display/src/DisplayObject.js index fdd5b09..ebcd232 100644 --- a/packages/display/src/DisplayObject.js +++ b/packages/display/src/DisplayObject.js @@ -75,6 +75,25 @@ this.worldAlpha = 1; /** + * Which index in the children array the display component was before the previous zIndex sort. + * Used by containers to help sort objects with the same zIndex, by using previous array index as the decider. + * + * @member {number} + * @private + * @readOnly + */ + this._lastSortedIndex = 0; + + /** + * The zIndex of the displayObject. + * A higher value will mean it will be rendered on top of other displayObjects within the same container. + * + * @member {number} + * @private + */ + this._zIndex = 0; + + /** * The area the filter is applied to. This is used as more of an optimization * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle. * @@ -571,6 +590,28 @@ } /** + * The zIndex of the displayObject. + * If a container has the sortableChildren property set to true, children will be automatically + * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array, + * and thus rendered on top of other displayObjects within the same container. + * + * @member {number} + */ + get zIndex() + { + return this._zIndex; + } + + set zIndex(value) // eslint-disable-line require-jsdoc + { + this._zIndex = value; + if (this.parent) + { + this.parent.sortDirty = true; + } + } + + /** * Indicates if the object is globally visible. * * @member {boolean} diff --git a/packages/display/src/index.js b/packages/display/src/index.js index 2d664ea..48acb62 100644 --- a/packages/display/src/index.js +++ b/packages/display/src/index.js @@ -1,3 +1,5 @@ +import './settings'; + export { default as Bounds } from './Bounds'; export { default as DisplayObject } from './DisplayObject'; export { default as Container } from './Container'; diff --git a/packages/display/src/settings.js b/packages/display/src/settings.js new file mode 100644 index 0000000..5de2e34 --- /dev/null +++ b/packages/display/src/settings.js @@ -0,0 +1,24 @@ +import { settings } from '@pixi/settings'; + +/** + * Sets the default value for the container property 'sortableChildren'. + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @static + * @constant + * @name SORTABLE_CHILDREN + * @memberof PIXI.settings + * @type {boolean} + * @default false + */ +settings.SORTABLE_CHILDREN = false; + +export { settings }; diff --git a/packages/display/package.json b/packages/display/package.json index ccb7bf2..c187fac 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -26,6 +26,7 @@ ], "dependencies": { "@pixi/math": "^5.0.0-alpha.3", + "@pixi/settings": "^5.0.0-alpha.3", "@pixi/utils": "^5.0.0-alpha.3", "eventemitter3": "^2.0.0", "remove-array-items": "^1.0.0" diff --git a/packages/display/src/Container.js b/packages/display/src/Container.js index 08dd670..119eb35 100644 --- a/packages/display/src/Container.js +++ b/packages/display/src/Container.js @@ -1,6 +1,17 @@ +import { settings } from '@pixi/settings'; import removeItems from 'remove-array-items'; import DisplayObject from './DisplayObject'; +function sortChildren(a, b) +{ + if (a.zIndex === b.zIndex) + { + return a._lastSortedIndex - b._lastSortedIndex; + } + + return a.zIndex - b.zIndex; +} + /** * A Container represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. @@ -30,6 +41,31 @@ * @readonly */ this.children = []; + + /** + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @see PIXI.settings.SORTABLE_CHILDREN + * + * @member {boolean} + */ + this.sortableChildren = settings.SORTABLE_CHILDREN; + + /** + * Should children be sorted by zIndex at the next updateTransform call. + * Will get automatically set to true if a new child is added, or if a child's zIndex changes. + * + * @member {boolean} + */ + this.sortDirty = false; } /** @@ -73,6 +109,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -109,6 +147,8 @@ } child.parent = this; + this.sortDirty = true; + // ensure child transform will be recalculated child.transform._parentID = -1; @@ -314,10 +354,42 @@ } /** + * Sorts children by zIndex. Previous order is mantained for 2 children with the same zIndex. + */ + sortChildren() + { + let sortRequired = false; + + for (let i = 0, j = this.children.length; i < j; ++i) + { + const child = this.children[i]; + + child._lastSortedIndex = i; + + if (!sortRequired && child.zIndex !== 0) + { + sortRequired = true; + } + } + + if (sortRequired && this.children.length > 1) + { + this.children.sort(sortChildren); + } + + this.sortDirty = false; + } + + /** * Updates the transform on all children of this container for rendering */ updateTransform() { + if (this.sortableChildren && this.sortDirty) + { + this.sortChildren(); + } + this._boundsID++; this.transform.updateTransform(this.parent.transform); @@ -508,6 +580,8 @@ { super.destroy(); + this.sortDirty = false; + const destroyChildren = typeof options === 'boolean' ? options : options && options.children; const oldChildren = this.removeChildren(0, this.children.length); diff --git a/packages/display/src/DisplayObject.js b/packages/display/src/DisplayObject.js index fdd5b09..ebcd232 100644 --- a/packages/display/src/DisplayObject.js +++ b/packages/display/src/DisplayObject.js @@ -75,6 +75,25 @@ this.worldAlpha = 1; /** + * Which index in the children array the display component was before the previous zIndex sort. + * Used by containers to help sort objects with the same zIndex, by using previous array index as the decider. + * + * @member {number} + * @private + * @readOnly + */ + this._lastSortedIndex = 0; + + /** + * The zIndex of the displayObject. + * A higher value will mean it will be rendered on top of other displayObjects within the same container. + * + * @member {number} + * @private + */ + this._zIndex = 0; + + /** * The area the filter is applied to. This is used as more of an optimization * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle. * @@ -571,6 +590,28 @@ } /** + * The zIndex of the displayObject. + * If a container has the sortableChildren property set to true, children will be automatically + * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array, + * and thus rendered on top of other displayObjects within the same container. + * + * @member {number} + */ + get zIndex() + { + return this._zIndex; + } + + set zIndex(value) // eslint-disable-line require-jsdoc + { + this._zIndex = value; + if (this.parent) + { + this.parent.sortDirty = true; + } + } + + /** * Indicates if the object is globally visible. * * @member {boolean} diff --git a/packages/display/src/index.js b/packages/display/src/index.js index 2d664ea..48acb62 100644 --- a/packages/display/src/index.js +++ b/packages/display/src/index.js @@ -1,3 +1,5 @@ +import './settings'; + export { default as Bounds } from './Bounds'; export { default as DisplayObject } from './DisplayObject'; export { default as Container } from './Container'; diff --git a/packages/display/src/settings.js b/packages/display/src/settings.js new file mode 100644 index 0000000..5de2e34 --- /dev/null +++ b/packages/display/src/settings.js @@ -0,0 +1,24 @@ +import { settings } from '@pixi/settings'; + +/** + * Sets the default value for the container property 'sortableChildren'. + * If set to true, the container will sort its children by zIndex value + * when updateTransform() is called, or manually if sortChildren() is called. + * + * This actually changes the order of elements in the array, so should be treated + * as a basic solution that is not performant compared to other solutions, + * such as @link https://github.com/pixijs/pixi-display + * + * Also be aware of that this may not work nicely with the addChildAt() function, + * as the zIndex sorting may cause the child to automatically sorted to another position. + * + * @static + * @constant + * @name SORTABLE_CHILDREN + * @memberof PIXI.settings + * @type {boolean} + * @default false + */ +settings.SORTABLE_CHILDREN = false; + +export { settings }; diff --git a/packages/display/test/Container.js b/packages/display/test/Container.js index 6c440ac..43baea4 100644 --- a/packages/display/test/Container.js +++ b/packages/display/test/Container.js @@ -430,6 +430,63 @@ }); }); + describe('updateTransform', function () + { + it('should call sortChildren if sortDirty and sortableChildren are true', function () + { + const parent = new Container(); + const container = new Container(); + const child = new Container(); + const canvasSpy = sinon.spy(container, 'sortChildren'); + + parent.addChild(container); + container.addChild(child); + + container.sortDirty = true; + container.sortableChildren = true; + + container.updateTransform(); + + expect(canvasSpy).to.have.been.called; + }); + + it('should not call sortChildren if sortDirty is false', function () + { + const parent = new Container(); + const container = new Container(); + const child = new Container(); + const canvasSpy = sinon.spy(container, 'sortChildren'); + + parent.addChild(container); + container.addChild(child); + + container.sortDirty = false; + container.sortableChildren = true; + + container.updateTransform(); + + expect(canvasSpy).to.not.have.been.called; + }); + + it('should not call sortChildren if sortableChildren is false', function () + { + const parent = new Container(); + const container = new Container(); + const child = new Container(); + const canvasSpy = sinon.spy(container, 'sortChildren'); + + parent.addChild(container); + container.addChild(child); + + container.sortDirty = true; + container.sortableChildren = false; + + container.updateTransform(); + + expect(canvasSpy).to.not.have.been.called; + }); + }); + describe('render', function () { it('should not render when object not visible', function () @@ -595,6 +652,263 @@ }); }); + describe('sortDirty', function () + { + it('should set sortDirty flag to true when adding a new child', function () + { + const parent = new Container(); + const child = new DisplayObject(); + + expect(parent.sortDirty).to.be.false; + + parent.addChild(child); + + expect(parent.sortDirty).to.be.true; + }); + + it('should set sortDirty flag to true when changing a child zIndex', function () + { + const parent = new Container(); + const child = new DisplayObject(); + + parent.addChild(child); + + parent.sortDirty = false; + + child.zIndex = 10; + + expect(parent.sortDirty).to.be.true; + }); + }); + + describe('sortChildren', function () + { + it('should reset sortDirty flag', function () + { + const container = new Container(); + + container.sortDirty = true; + + container.sortChildren(); + + expect(container.sortDirty).to.be.false; + }); + + it('should call sort when at least one child has a zIndex', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const spy = sinon.spy(container.children, 'sort'); + + child1.zIndex = 5; + container.addChild(child1, child2); + + container.sortChildren(); + + expect(spy).to.have.been.called; + }); + + it('should not call sort when children have no zIndex', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const spy = sinon.spy(container.children, 'sort'); + + container.addChild(child1, child2); + + container.sortChildren(); + + expect(spy).to.not.have.been.called; + }); + + it('should sort children by zIndex value', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const child3 = new DisplayObject(); + const child4 = new DisplayObject(); + + child1.zIndex = 20; + child2.zIndex = 10; + child3.zIndex = 15; + + container.addChild(child1, child2, child3, child4); + + expect(container.children.indexOf(child1)).to.be.equals(0); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(3); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(3); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(0); + }); + + it('should sort children by current array order if zIndex values match', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const child3 = new DisplayObject(); + const child4 = new DisplayObject(); + + child1.zIndex = 20; + child2.zIndex = 20; + child3.zIndex = 10; + child4.zIndex = 10; + + container.addChild(child1, child2, child3, child4); + + expect(container.children.indexOf(child1)).to.be.equals(0); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(3); + + container.sortChildren(); + + expect(child1._lastSortedIndex).to.be.equals(0); + expect(child2._lastSortedIndex).to.be.equals(1); + expect(child3._lastSortedIndex).to.be.equals(2); + expect(child4._lastSortedIndex).to.be.equals(3); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(3); + expect(container.children.indexOf(child3)).to.be.equals(0); + expect(container.children.indexOf(child4)).to.be.equals(1); + }); + + it('should sort children in the same way despite being called multiple times', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const child3 = new DisplayObject(); + const child4 = new DisplayObject(); + + child1.zIndex = 10; + child2.zIndex = 15; + child3.zIndex = 5; + child4.zIndex = 0; + + container.addChild(child1, child2, child3, child4); + + expect(container.children.indexOf(child1)).to.be.equals(0); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(3); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(3); + expect(container.children.indexOf(child3)).to.be.equals(1); + expect(container.children.indexOf(child4)).to.be.equals(0); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(3); + expect(container.children.indexOf(child3)).to.be.equals(1); + expect(container.children.indexOf(child4)).to.be.equals(0); + + child1.zIndex = 1; + child2.zIndex = 1; + child3.zIndex = 1; + child4.zIndex = 1; + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(3); + expect(container.children.indexOf(child3)).to.be.equals(1); + expect(container.children.indexOf(child4)).to.be.equals(0); + }); + + it('should sort new children added correctly', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const child3 = new DisplayObject(); + const child4 = new DisplayObject(); + + child1.zIndex = 20; + child2.zIndex = 10; + child3.zIndex = 15; + + container.addChild(child1, child2, child3); + + expect(container.children.indexOf(child1)).to.be.equals(0); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(0); + expect(container.children.indexOf(child3)).to.be.equals(1); + + container.addChild(child4); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(0); + expect(container.children.indexOf(child3)).to.be.equals(1); + expect(container.children.indexOf(child4)).to.be.equals(3); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(3); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(0); + }); + + it('should sort children after a removal correctly', function () + { + const container = new Container(); + const child1 = new DisplayObject(); + const child2 = new DisplayObject(); + const child3 = new DisplayObject(); + const child4 = new DisplayObject(); + + child1.zIndex = 20; + child2.zIndex = 10; + child3.zIndex = 15; + + container.addChild(child1, child2, child3, child4); + + expect(container.children.indexOf(child1)).to.be.equals(0); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(3); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(3); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child3)).to.be.equals(2); + expect(container.children.indexOf(child4)).to.be.equals(0); + + container.removeChild(child3); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child4)).to.be.equals(0); + + container.sortChildren(); + + expect(container.children.indexOf(child1)).to.be.equals(2); + expect(container.children.indexOf(child2)).to.be.equals(1); + expect(container.children.indexOf(child4)).to.be.equals(0); + }); + }); + function assertRemovedFromParent(parent, container, child, functionToAssert) { parent.addChild(child);