pixi.js
Version:
<p align="center"> <a href="https://pixijs.com" target="_blank" rel="noopener noreferrer"> <img height="150" src="https://files.pixijs.download/branding/pixijs-logo-transparent-dark.svg?v=1" alt="PixiJS logo"> </a> </p> <br/> <p align="center">
1,132 lines (1,128 loc) • 36.8 kB
JavaScript
'use strict';
var EventEmitter = require('eventemitter3');
var Color = require('../../color/Color.js');
var cullingMixin = require('../../culling/cullingMixin.js');
var Extensions = require('../../extensions/Extensions.js');
var Matrix = require('../../maths/matrix/Matrix.js');
var _const = require('../../maths/misc/const.js');
var ObservablePoint = require('../../maths/point/ObservablePoint.js');
var uid = require('../../utils/data/uid.js');
var deprecation = require('../../utils/logging/deprecation.js');
var PoolGroup = require('../../utils/pool/PoolGroup.js');
var cacheAsTextureMixin = require('./container-mixins/cacheAsTextureMixin.js');
var childrenHelperMixin = require('./container-mixins/childrenHelperMixin.js');
var collectRenderablesMixin = require('./container-mixins/collectRenderablesMixin.js');
var effectsMixin = require('./container-mixins/effectsMixin.js');
var findMixin = require('./container-mixins/findMixin.js');
var getFastGlobalBoundsMixin = require('./container-mixins/getFastGlobalBoundsMixin.js');
var getGlobalMixin = require('./container-mixins/getGlobalMixin.js');
var measureMixin = require('./container-mixins/measureMixin.js');
var onRenderMixin = require('./container-mixins/onRenderMixin.js');
var sortMixin = require('./container-mixins/sortMixin.js');
var toLocalGlobalMixin = require('./container-mixins/toLocalGlobalMixin.js');
var RenderGroup = require('./RenderGroup.js');
var assignWithIgnore = require('./utils/assignWithIgnore.js');
"use strict";
const defaultSkew = new ObservablePoint.ObservablePoint(null);
const defaultPivot = new ObservablePoint.ObservablePoint(null);
const defaultScale = new ObservablePoint.ObservablePoint(null, 1, 1);
const UPDATE_COLOR = 1;
const UPDATE_BLEND = 2;
const UPDATE_VISIBLE = 4;
const UPDATE_TRANSFORM = 8;
class Container extends EventEmitter {
constructor(options = {}) {
super();
/**
* unique id for this container
* @internal
*/
this.uid = uid.uid("renderable");
/** @private */
this._updateFlags = 15;
// the render group this container owns
/** @private */
this.renderGroup = null;
// the render group this container belongs to
/** @private */
this.parentRenderGroup = null;
// the index of the container in the render group
/** @private */
this.parentRenderGroupIndex = 0;
// set to true if the container has changed. It is reset once the changes have been applied
// by the transform system
// its here to stop ensure that when things change, only one update gets registers with the transform system
/** @private */
this.didChange = false;
// same as above, but for the renderable
/** @private */
this.didViewUpdate = false;
// how deep is the container relative to its render group..
// unless the element is the root render group - it will be relative to its parent
/** @private */
this.relativeRenderGroupDepth = 0;
/**
* The array of children of this container. Each child must be a Container or extend from it.
*
* The array is read-only, but its contents can be modified using Container methods.
* @example
* ```ts
* // Access children
* const firstChild = container.children[0];
* const lastChild = container.children[container.children.length - 1];
* ```
* @readonly
* @see {@link Container#addChild} For adding children
* @see {@link Container#removeChild} For removing children
*/
this.children = [];
/**
* The display object container that contains this display object.
* This represents the parent-child relationship in the display tree.
* @example
* ```ts
* // Basic parent access
* const parent = sprite.parent;
*
* // Walk up the tree
* let current = sprite;
* while (current.parent) {
* console.log('Level up:', current.parent.constructor.name);
* current = current.parent;
* }
* ```
* @readonly
* @see {@link Container#addChild} For adding to a parent
* @see {@link Container#removeChild} For removing from parent
*/
this.parent = null;
// used internally for changing up the render order.. mainly for masks and filters
// TODO setting this should cause a rebuild??
/** @private */
this.includeInBuild = true;
/** @private */
this.measurable = true;
/** @private */
this.isSimple = true;
// / /////////////Transform related props//////////////
// used by the transform system to check if a container needs to be updated that frame
// if the tick matches the current transform system tick, it is not updated again
/** @internal */
this.updateTick = -1;
/**
* Current transform of the object based on local factors: position, scale, other stuff.
* This matrix represents the local transformation without any parent influence.
* @example
* ```ts
* // Basic transform access
* const localMatrix = sprite.localTransform;
* console.log(localMatrix.toString());
* ```
* @readonly
* @see {@link Container#worldTransform} For global transform
* @see {@link Container#groupTransform} For render group transform
*/
this.localTransform = new Matrix.Matrix();
/**
* The relative group transform is a transform relative to the render group it belongs too. It will include all parent
* transforms and up to the render group (think of it as kind of like a stage - but the stage can be nested).
* If this container is is self a render group matrix will be relative to its parent render group
* @readonly
* @advanced
*/
this.relativeGroupTransform = new Matrix.Matrix();
/**
* The group transform is a transform relative to the render group it belongs too.
* If this container is render group then this will be an identity matrix. other wise it
* will be the same as the relativeGroupTransform.
* Use this value when actually rendering things to the screen
* @readonly
* @advanced
*/
this.groupTransform = this.relativeGroupTransform;
/**
* Whether this object has been destroyed. If true, the object should no longer be used.
* After an object is destroyed, all of its functionality is disabled and references are removed.
* @example
* ```ts
* // Cleanup with destroy
* sprite.destroy();
* console.log(sprite.destroyed); // true
* ```
* @default false
* @see {@link Container#destroy} For destroying objects
*/
this.destroyed = false;
// transform data..
/**
* The coordinate of the object relative to the local coordinates of the parent.
* @internal
*/
this._position = new ObservablePoint.ObservablePoint(this, 0, 0);
/**
* The scale factor of the object.
* @internal
*/
this._scale = defaultScale;
/**
* The pivot point of the container that it rotates around.
* @internal
*/
this._pivot = defaultPivot;
/**
* The skew amount, on the x and y axis.
* @internal
*/
this._skew = defaultSkew;
/**
* The X-coordinate value of the normalized local X axis,
* the first column of the local transformation matrix without a scale.
* @internal
*/
this._cx = 1;
/**
* The Y-coordinate value of the normalized local X axis,
* the first column of the local transformation matrix without a scale.
* @internal
*/
this._sx = 0;
/**
* The X-coordinate value of the normalized local Y axis,
* the second column of the local transformation matrix without a scale.
* @internal
*/
this._cy = 0;
/**
* The Y-coordinate value of the normalized local Y axis,
* the second column of the local transformation matrix without a scale.
* @internal
*/
this._sy = 1;
/**
* The rotation amount.
* @internal
*/
this._rotation = 0;
// / COLOR related props //////////////
// color stored as ABGR
/** @internal */
this.localColor = 16777215;
/** @internal */
this.localAlpha = 1;
/** @internal */
this.groupAlpha = 1;
// A
/** @internal */
this.groupColor = 16777215;
// BGR
/** @internal */
this.groupColorAlpha = 4294967295;
// ABGR
// / BLEND related props //////////////
/** @internal */
this.localBlendMode = "inherit";
/** @internal */
this.groupBlendMode = "normal";
// / VISIBILITY related props //////////////
// visibility
// 0b11
// first bit is visible, second bit is renderable
/**
* This property holds three bits: culled, visible, renderable
* the third bit represents culling (0 = culled, 1 = not culled) 0b100
* the second bit represents visibility (0 = not visible, 1 = visible) 0b010
* the first bit represents renderable (0 = not renderable, 1 = renderable) 0b001
* @internal
*/
this.localDisplayStatus = 7;
// 0b11 | 0b10 | 0b01 | 0b00
/** @internal */
this.globalDisplayStatus = 7;
/**
* A value that increments each time the containe is modified
* eg children added, removed etc
* @ignore
*/
this._didContainerChangeTick = 0;
/**
* A value that increments each time the container view is modified
* eg texture swap, geometry change etc
* @ignore
*/
this._didViewChangeTick = 0;
/**
* property that tracks if the container transform has changed
* @ignore
*/
this._didLocalTransformChangeId = -1;
this.effects = [];
assignWithIgnore.assignWithIgnore(this, options, {
children: true,
parent: true,
effects: true
});
options.children?.forEach((child) => this.addChild(child));
options.parent?.addChild(this);
}
/**
* Mixes all enumerable properties and methods from a source object to Container.
* @param source - The source of properties and methods to mix in.
* @deprecated since 8.8.0
*/
static mixin(source) {
deprecation.deprecation("8.8.0", "Container.mixin is deprecated, please use extensions.mixin instead.");
Extensions.extensions.mixin(Container, source);
}
// = 'default';
/**
* We now use the _didContainerChangeTick and _didViewChangeTick to track changes
* @deprecated since 8.2.6
* @ignore
*/
set _didChangeId(value) {
this._didViewChangeTick = value >> 12 & 4095;
this._didContainerChangeTick = value & 4095;
}
/** @ignore */
get _didChangeId() {
return this._didContainerChangeTick & 4095 | (this._didViewChangeTick & 4095) << 12;
}
/**
* Adds one or more children to the container.
* The children will be rendered as part of this container's display list.
* @example
* ```ts
* // Add a single child
* container.addChild(sprite);
*
* // Add multiple children
* container.addChild(background, player, foreground);
*
* // Add with type checking
* const sprite = container.addChild<Sprite>(new Sprite(texture));
* sprite.tint = 'red';
* ```
* @param children - The Container(s) to add to the container
* @returns The first child that was added
* @see {@link Container#removeChild} For removing children
* @see {@link Container#addChildAt} For adding at specific index
*/
addChild(...children) {
if (!this.allowChildren) {
deprecation.deprecation(deprecation.v8_0_0, "addChild: Only Containers will be allowed to add children in v8.0.0");
}
if (children.length > 1) {
for (let i = 0; i < children.length; i++) {
this.addChild(children[i]);
}
return children[0];
}
const child = children[0];
const renderGroup = this.renderGroup || this.parentRenderGroup;
if (child.parent === this) {
this.children.splice(this.children.indexOf(child), 1);
this.children.push(child);
if (renderGroup) {
renderGroup.structureDidChange = true;
}
return child;
}
if (child.parent) {
child.parent.removeChild(child);
}
this.children.push(child);
if (this.sortableChildren)
this.sortDirty = true;
child.parent = this;
child.didChange = true;
child._updateFlags = 15;
if (renderGroup) {
renderGroup.addChild(child);
}
this.emit("childAdded", child, this, this.children.length - 1);
child.emit("added", this);
this._didViewChangeTick++;
if (child._zIndex !== 0) {
child.depthOfChildModified();
}
return child;
}
/**
* Removes one or more children from the container.
* When removing multiple children, events will be triggered for each child in sequence.
* @example
* ```ts
* // Remove a single child
* const removed = container.removeChild(sprite);
*
* // Remove multiple children
* const bg = container.removeChild(background, player, userInterface);
*
* // Remove with type checking
* const sprite = container.removeChild<Sprite>(childSprite);
* sprite.texture = newTexture;
* ```
* @param children - The Container(s) to remove
* @returns The first child that was removed
* @see {@link Container#addChild} For adding children
* @see {@link Container#removeChildren} For removing multiple children
*/
removeChild(...children) {
if (children.length > 1) {
for (let i = 0; i < children.length; i++) {
this.removeChild(children[i]);
}
return children[0];
}
const child = children[0];
const index = this.children.indexOf(child);
if (index > -1) {
this._didViewChangeTick++;
this.children.splice(index, 1);
if (this.renderGroup) {
this.renderGroup.removeChild(child);
} else if (this.parentRenderGroup) {
this.parentRenderGroup.removeChild(child);
}
if (child.parentRenderLayer) {
child.parentRenderLayer.detach(child);
}
child.parent = null;
this.emit("childRemoved", child, this, index);
child.emit("removed", this);
}
return child;
}
/** @ignore */
_onUpdate(point) {
if (point) {
if (point === this._skew) {
this._updateSkew();
}
}
this._didContainerChangeTick++;
if (this.didChange)
return;
this.didChange = true;
if (this.parentRenderGroup) {
this.parentRenderGroup.onChildUpdate(this);
}
}
set isRenderGroup(value) {
if (!!this.renderGroup === value)
return;
if (value) {
this.enableRenderGroup();
} else {
this.disableRenderGroup();
}
}
/**
* Returns true if this container is a render group.
* This means that it will be rendered as a separate pass, with its own set of instructions
* @advanced
*/
get isRenderGroup() {
return !!this.renderGroup;
}
/**
* Calling this enables a render group for this container.
* This means it will be rendered as a separate set of instructions.
* The transform of the container will also be handled on the GPU rather than the CPU.
* @advanced
*/
enableRenderGroup() {
if (this.renderGroup)
return;
const parentRenderGroup = this.parentRenderGroup;
parentRenderGroup?.removeChild(this);
this.renderGroup = PoolGroup.BigPool.get(RenderGroup.RenderGroup, this);
this.groupTransform = Matrix.Matrix.IDENTITY;
parentRenderGroup?.addChild(this);
this._updateIsSimple();
}
/**
* This will disable the render group for this container.
* @advanced
*/
disableRenderGroup() {
if (!this.renderGroup)
return;
const parentRenderGroup = this.parentRenderGroup;
parentRenderGroup?.removeChild(this);
PoolGroup.BigPool.return(this.renderGroup);
this.renderGroup = null;
this.groupTransform = this.relativeGroupTransform;
parentRenderGroup?.addChild(this);
this._updateIsSimple();
}
/** @ignore */
_updateIsSimple() {
this.isSimple = !this.renderGroup && this.effects.length === 0;
}
/**
* Current transform of the object based on world (parent) factors.
*
* This matrix represents the absolute transformation in the scene graph.
* @example
* ```ts
* // Get world position
* const worldPos = container.worldTransform;
* console.log(`World position: (${worldPos.tx}, ${worldPos.ty})`);
* ```
* @readonly
* @see {@link Container#localTransform} For local space transform
*/
get worldTransform() {
this._worldTransform || (this._worldTransform = new Matrix.Matrix());
if (this.renderGroup) {
this._worldTransform.copyFrom(this.renderGroup.worldTransform);
} else if (this.parentRenderGroup) {
this._worldTransform.appendFrom(this.relativeGroupTransform, this.parentRenderGroup.worldTransform);
}
return this._worldTransform;
}
/**
* The position of the container on the x axis relative to the local coordinates of the parent.
*
* An alias to position.x
* @example
* ```ts
* // Basic position
* container.x = 100;
* ```
*/
get x() {
return this._position.x;
}
set x(value) {
this._position.x = value;
}
/**
* The position of the container on the y axis relative to the local coordinates of the parent.
*
* An alias to position.y
* @example
* ```ts
* // Basic position
* container.y = 200;
* ```
*/
get y() {
return this._position.y;
}
set y(value) {
this._position.y = value;
}
/**
* The coordinate of the object relative to the local coordinates of the parent.
* @example
* ```ts
* // Basic position setting
* container.position.set(100, 200);
* container.position.set(100); // Sets both x and y to 100
* // Using point data
* container.position = { x: 50, y: 75 };
* ```
* @since 4.0.0
*/
get position() {
return this._position;
}
set position(value) {
this._position.copyFrom(value);
}
/**
* The rotation of the object in radians.
*
* > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;
* > rotation is in radians, angle is in degrees.
* @example
* ```ts
* // Basic rotation
* container.rotation = Math.PI / 4; // 45 degrees
*
* // Convert from degrees
* const degrees = 45;
* container.rotation = degrees * Math.PI / 180;
*
* // Rotate around center
* container.pivot.set(container.width / 2, container.height / 2);
* container.rotation = Math.PI; // 180 degrees
* ```
*/
get rotation() {
return this._rotation;
}
set rotation(value) {
if (this._rotation !== value) {
this._rotation = value;
this._onUpdate(this._skew);
}
}
/**
* The angle of the object in degrees.
*
* > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;
* > rotation is in radians, angle is in degrees.
@example
* ```ts
* // Basic angle rotation
* sprite.angle = 45; // 45 degrees
*
* // Rotate around center
* sprite.pivot.set(sprite.width / 2, sprite.height / 2);
* sprite.angle = 180; // Half rotation
*
* // Reset rotation
* sprite.angle = 0;
* ```
*/
get angle() {
return this.rotation * _const.RAD_TO_DEG;
}
set angle(value) {
this.rotation = value * _const.DEG_TO_RAD;
}
/**
* The center of rotation, scaling, and skewing for this display object in its local space.
* The `position` is the projection of `pivot` in the parent's local space.
*
* By default, the pivot is the origin (0, 0).
* @example
* ```ts
* // Rotate around center
* container.pivot.set(container.width / 2, container.height / 2);
* container.rotation = Math.PI; // Rotates around center
* ```
* @since 4.0.0
*/
get pivot() {
if (this._pivot === defaultPivot) {
this._pivot = new ObservablePoint.ObservablePoint(this, 0, 0);
}
return this._pivot;
}
set pivot(value) {
if (this._pivot === defaultPivot) {
this._pivot = new ObservablePoint.ObservablePoint(this, 0, 0);
}
typeof value === "number" ? this._pivot.set(value) : this._pivot.copyFrom(value);
}
/**
* The skew factor for the object in radians. Skewing is a transformation that distorts
* the object by rotating it differently at each point, creating a non-uniform shape.
* @example
* ```ts
* // Basic skewing
* container.skew.set(0.5, 0); // Skew horizontally
* container.skew.set(0, 0.5); // Skew vertically
*
* // Skew with point data
* container.skew = { x: 0.3, y: 0.3 }; // Diagonal skew
*
* // Reset skew
* container.skew.set(0, 0);
*
* // Animate skew
* app.ticker.add(() => {
* // Create wave effect
* container.skew.x = Math.sin(Date.now() / 1000) * 0.3;
* });
*
* // Combine with rotation
* container.rotation = Math.PI / 4; // 45 degrees
* container.skew.set(0.2, 0.2); // Skew the rotated object
* ```
* @since 4.0.0
* @type {ObservablePoint} Point-like object with x/y properties in radians
* @default {x: 0, y: 0}
*/
get skew() {
if (this._skew === defaultSkew) {
this._skew = new ObservablePoint.ObservablePoint(this, 0, 0);
}
return this._skew;
}
set skew(value) {
if (this._skew === defaultSkew) {
this._skew = new ObservablePoint.ObservablePoint(this, 0, 0);
}
this._skew.copyFrom(value);
}
/**
* The scale factors of this object along the local coordinate axes.
*
* The default scale is (1, 1).
* @example
* ```ts
* // Basic scaling
* container.scale.set(2, 2); // Scales to double size
* container.scale.set(2); // Scales uniformly to double size
* container.scale = 2; // Scales uniformly to double size
* // Scale to a specific width and height
* container.setSize(200, 100); // Sets width to 200 and height to 100
* ```
* @since 4.0.0
*/
get scale() {
if (this._scale === defaultScale) {
this._scale = new ObservablePoint.ObservablePoint(this, 1, 1);
}
return this._scale;
}
set scale(value) {
if (this._scale === defaultScale) {
this._scale = new ObservablePoint.ObservablePoint(this, 0, 0);
}
typeof value === "number" ? this._scale.set(value) : this._scale.copyFrom(value);
}
/**
* The width of the Container, setting this will actually modify the scale to achieve the value set.
* > [!NOTE] Changing the width will adjust the scale.x property of the container while maintaining its aspect ratio.
* > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize}
* as it is more optimized by not recalculating the local bounds twice.
* @example
* ```ts
* // Basic width setting
* container.width = 100;
* // Optimized width setting
* container.setSize(100, 100);
* ```
*/
get width() {
return Math.abs(this.scale.x * this.getLocalBounds().width);
}
set width(value) {
const localWidth = this.getLocalBounds().width;
this._setWidth(value, localWidth);
}
/**
* The height of the Container,
* > [!NOTE] Changing the height will adjust the scale.y property of the container while maintaining its aspect ratio.
* > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize}
* as it is more optimized by not recalculating the local bounds twice.
* @example
* ```ts
* // Basic height setting
* container.height = 200;
* // Optimized height setting
* container.setSize(100, 200);
* ```
*/
get height() {
return Math.abs(this.scale.y * this.getLocalBounds().height);
}
set height(value) {
const localHeight = this.getLocalBounds().height;
this._setHeight(value, localHeight);
}
/**
* Retrieves the size of the container as a [Size]{@link Size} object.
*
* This is faster than get the width and height separately.
* @example
* ```ts
* // Basic size retrieval
* const size = container.getSize();
* console.log(`Size: ${size.width}x${size.height}`);
*
* // Reuse existing size object
* const reuseSize = { width: 0, height: 0 };
* container.getSize(reuseSize);
* ```
* @param out - Optional object to store the size in.
* @returns - The size of the container.
*/
getSize(out) {
if (!out) {
out = {};
}
const bounds = this.getLocalBounds();
out.width = Math.abs(this.scale.x * bounds.width);
out.height = Math.abs(this.scale.y * bounds.height);
return out;
}
/**
* Sets the size of the container to the specified width and height.
* This is more efficient than setting width and height separately as it only recalculates bounds once.
* @example
* ```ts
* // Basic size setting
* container.setSize(100, 200);
*
* // Set uniform size
* container.setSize(100); // Sets both width and height to 100
* ```
* @param value - This can be either a number or a [Size]{@link Size} object.
* @param height - The height to set. Defaults to the value of `width` if not provided.
*/
setSize(value, height) {
const size = this.getLocalBounds();
if (typeof value === "object") {
height = value.height ?? value.width;
value = value.width;
} else {
height ?? (height = value);
}
value !== void 0 && this._setWidth(value, size.width);
height !== void 0 && this._setHeight(height, size.height);
}
/** Called when the skew or the rotation changes. */
_updateSkew() {
const rotation = this._rotation;
const skew = this._skew;
this._cx = Math.cos(rotation + skew._y);
this._sx = Math.sin(rotation + skew._y);
this._cy = -Math.sin(rotation - skew._x);
this._sy = Math.cos(rotation - skew._x);
}
/**
* Updates the transform properties of the container.
* Allows partial updates of transform properties for optimized manipulation.
* @example
* ```ts
* // Basic transform update
* container.updateTransform({
* x: 100,
* y: 200,
* rotation: Math.PI / 4
* });
*
* // Scale and rotate around center
* sprite.updateTransform({
* pivotX: sprite.width / 2,
* pivotY: sprite.height / 2,
* scaleX: 2,
* scaleY: 2,
* rotation: Math.PI
* });
*
* // Update position only
* button.updateTransform({
* x: button.x + 10, // Move right
* y: button.y // Keep same y
* });
* ```
* @param opts - Transform options to update
* @param opts.x - The x position
* @param opts.y - The y position
* @param opts.scaleX - The x-axis scale factor
* @param opts.scaleY - The y-axis scale factor
* @param opts.rotation - The rotation in radians
* @param opts.skewX - The x-axis skew factor
* @param opts.skewY - The y-axis skew factor
* @param opts.pivotX - The x-axis pivot point
* @param opts.pivotY - The y-axis pivot point
* @returns This container, for chaining
* @see {@link Container#setFromMatrix} For matrix-based transforms
* @see {@link Container#position} For direct position access
*/
updateTransform(opts) {
this.position.set(
typeof opts.x === "number" ? opts.x : this.position.x,
typeof opts.y === "number" ? opts.y : this.position.y
);
this.scale.set(
typeof opts.scaleX === "number" ? opts.scaleX || 1 : this.scale.x,
typeof opts.scaleY === "number" ? opts.scaleY || 1 : this.scale.y
);
this.rotation = typeof opts.rotation === "number" ? opts.rotation : this.rotation;
this.skew.set(
typeof opts.skewX === "number" ? opts.skewX : this.skew.x,
typeof opts.skewY === "number" ? opts.skewY : this.skew.y
);
this.pivot.set(
typeof opts.pivotX === "number" ? opts.pivotX : this.pivot.x,
typeof opts.pivotY === "number" ? opts.pivotY : this.pivot.y
);
return this;
}
/**
* Updates the local transform properties by decomposing the given matrix.
* Extracts position, scale, rotation, and skew from a transformation matrix.
* @example
* ```ts
* // Basic matrix transform
* const matrix = new Matrix()
* .translate(100, 100)
* .rotate(Math.PI / 4)
* .scale(2, 2);
*
* container.setFromMatrix(matrix);
*
* // Copy transform from another container
* const source = new Container();
* source.position.set(100, 100);
* source.rotation = Math.PI / 2;
*
* target.setFromMatrix(source.localTransform);
*
* // Reset transform
* container.setFromMatrix(Matrix.IDENTITY);
* ```
* @param matrix - The matrix to use for updating the transform
* @see {@link Container#updateTransform} For property-based updates
* @see {@link Matrix#decompose} For matrix decomposition details
*/
setFromMatrix(matrix) {
matrix.decompose(this);
}
/** Updates the local transform. */
updateLocalTransform() {
const localTransformChangeId = this._didContainerChangeTick;
if (this._didLocalTransformChangeId === localTransformChangeId)
return;
this._didLocalTransformChangeId = localTransformChangeId;
const lt = this.localTransform;
const scale = this._scale;
const pivot = this._pivot;
const position = this._position;
const sx = scale._x;
const sy = scale._y;
const px = pivot._x;
const py = pivot._y;
lt.a = this._cx * sx;
lt.b = this._sx * sx;
lt.c = this._cy * sy;
lt.d = this._sy * sy;
lt.tx = position._x - (px * lt.a + py * lt.c);
lt.ty = position._y - (px * lt.b + py * lt.d);
}
// / ///// color related stuff
set alpha(value) {
if (value === this.localAlpha)
return;
this.localAlpha = value;
this._updateFlags |= UPDATE_COLOR;
this._onUpdate();
}
/**
* The opacity of the object relative to its parent's opacity.
* Value ranges from 0 (fully transparent) to 1 (fully opaque).
* @example
* ```ts
* // Basic transparency
* sprite.alpha = 0.5; // 50% opacity
*
* // Inherited opacity
* container.alpha = 0.5;
* const child = new Sprite(texture);
* child.alpha = 0.5;
* container.addChild(child);
* // child's effective opacity is 0.25 (0.5 * 0.5)
* ```
* @default 1
* @see {@link Container#visible} For toggling visibility
* @see {@link Container#renderable} For render control
*/
get alpha() {
return this.localAlpha;
}
set tint(value) {
const tempColor = Color.Color.shared.setValue(value ?? 16777215);
const bgr = tempColor.toBgrNumber();
if (bgr === this.localColor)
return;
this.localColor = bgr;
this._updateFlags |= UPDATE_COLOR;
this._onUpdate();
}
/**
* The tint applied to the sprite.
*
* This can be any valid {@link ColorSource}.
* @example
* ```ts
* // Basic color tinting
* container.tint = 0xff0000; // Red tint
* container.tint = 'red'; // Same as above
* container.tint = '#00ff00'; // Green
* container.tint = 'rgb(0,0,255)'; // Blue
*
* // Remove tint
* container.tint = 0xffffff; // White = no tint
* container.tint = null; // Also removes tint
* ```
* @default 0xFFFFFF
* @see {@link Container#alpha} For transparency
* @see {@link Container#visible} For visibility control
*/
get tint() {
return getGlobalMixin.bgr2rgb(this.localColor);
}
// / //////////////// blend related stuff
set blendMode(value) {
if (this.localBlendMode === value)
return;
if (this.parentRenderGroup) {
this.parentRenderGroup.structureDidChange = true;
}
this._updateFlags |= UPDATE_BLEND;
this.localBlendMode = value;
this._onUpdate();
}
/**
* The blend mode to be applied to the sprite. Controls how pixels are blended when rendering.
*
* Setting to 'normal' will reset to default blending.
* > [!NOTE] More blend modes are available after importing the `pixi.js/advanced-blend-modes` sub-export.
* @example
* ```ts
* // Basic blend modes
* sprite.blendMode = 'add'; // Additive blending
* sprite.blendMode = 'multiply'; // Multiply colors
* sprite.blendMode = 'screen'; // Screen blend
*
* // Reset blend mode
* sprite.blendMode = 'normal'; // Normal blending
* ```
* @default 'normal'
* @see {@link Container#alpha} For transparency
* @see {@link Container#tint} For color adjustments
*/
get blendMode() {
return this.localBlendMode;
}
// / ///////// VISIBILITY / RENDERABLE /////////////////
/**
* The visibility of the object. If false the object will not be drawn,
* and the transform will not be updated.
* @example
* ```ts
* // Basic visibility toggle
* sprite.visible = false; // Hide sprite
* sprite.visible = true; // Show sprite
* ```
* @default true
* @see {@link Container#renderable} For render-only control
* @see {@link Container#alpha} For transparency
*/
get visible() {
return !!(this.localDisplayStatus & 2);
}
set visible(value) {
const valueNumber = value ? 2 : 0;
if ((this.localDisplayStatus & 2) === valueNumber)
return;
if (this.parentRenderGroup) {
this.parentRenderGroup.structureDidChange = true;
}
this._updateFlags |= UPDATE_VISIBLE;
this.localDisplayStatus ^= 2;
this._onUpdate();
}
/** @ignore */
get culled() {
return !(this.localDisplayStatus & 4);
}
/** @ignore */
set culled(value) {
const valueNumber = value ? 0 : 4;
if ((this.localDisplayStatus & 4) === valueNumber)
return;
if (this.parentRenderGroup) {
this.parentRenderGroup.structureDidChange = true;
}
this._updateFlags |= UPDATE_VISIBLE;
this.localDisplayStatus ^= 4;
this._onUpdate();
}
/**
* Controls whether this object can be rendered. If false the object will not be drawn,
* but the transform will still be updated. This is different from visible, which skips
* transform updates.
* @example
* ```ts
* // Basic render control
* sprite.renderable = false; // Skip rendering
* sprite.renderable = true; // Enable rendering
* ```
* @default true
* @see {@link Container#visible} For skipping transform updates
* @see {@link Container#alpha} For transparency
*/
get renderable() {
return !!(this.localDisplayStatus & 1);
}
set renderable(value) {
const valueNumber = value ? 1 : 0;
if ((this.localDisplayStatus & 1) === valueNumber)
return;
this._updateFlags |= UPDATE_VISIBLE;
this.localDisplayStatus ^= 1;
if (this.parentRenderGroup) {
this.parentRenderGroup.structureDidChange = true;
}
this._onUpdate();
}
/**
* Whether or not the object should be rendered.
* @advanced
*/
get isRenderable() {
return this.localDisplayStatus === 7 && this.groupAlpha > 0;
}
/**
* Removes all internal references and listeners as well as removes children from the display list.
* Do not use a Container after calling `destroy`.
* @param options - Options parameter. A boolean will act as if all options
* have been set to that value
* @example
* ```ts
* container.destroy();
* container.destroy(true);
* container.destroy({ children: true });
* container.destroy({ children: true, texture: true, textureSource: true });
* ```
*/
destroy(options = false) {
if (this.destroyed)
return;
this.destroyed = true;
let oldChildren;
if (this.children.length) {
oldChildren = this.removeChildren(0, this.children.length);
}
this.removeFromParent();
this.parent = null;
this._maskEffect = null;
this._filterEffect = null;
this.effects = null;
this._position = null;
this._scale = null;
this._pivot = null;
this._skew = null;
this.emit("destroyed", this);
this.removeAllListeners();
const destroyChildren = typeof options === "boolean" ? options : options?.children;
if (destroyChildren && oldChildren) {
for (let i = 0; i < oldChildren.length; ++i) {
oldChildren[i].destroy(options);
}
}
this.renderGroup?.destroy();
this.renderGroup = null;
}
}
Extensions.extensions.mixin(
Container,
childrenHelperMixin.childrenHelperMixin,
getFastGlobalBoundsMixin.getFastGlobalBoundsMixin,
toLocalGlobalMixin.toLocalGlobalMixin,
onRenderMixin.onRenderMixin,
measureMixin.measureMixin,
effectsMixin.effectsMixin,
findMixin.findMixin,
sortMixin.sortMixin,
cullingMixin.cullingMixin,
cacheAsTextureMixin.cacheAsTextureMixin,
getGlobalMixin.getGlobalMixin,
collectRenderablesMixin.collectRenderablesMixin
);
exports.Container = Container;
exports.UPDATE_BLEND = UPDATE_BLEND;
exports.UPDATE_COLOR = UPDATE_COLOR;
exports.UPDATE_TRANSFORM = UPDATE_TRANSFORM;
exports.UPDATE_VISIBLE = UPDATE_VISIBLE;
//# sourceMappingURL=Container.js.map