UNPKG

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 lines 88.7 kB
{"version":3,"file":"Container.mjs","sources":["../../../src/scene/container/Container.ts"],"sourcesContent":["import EventEmitter from 'eventemitter3';\nimport { Color, type ColorSource } from '../../color/Color';\nimport { cullingMixin } from '../../culling/cullingMixin';\nimport { extensions } from '../../extensions/Extensions';\nimport { Matrix } from '../../maths/matrix/Matrix';\nimport { DEG_TO_RAD, RAD_TO_DEG } from '../../maths/misc/const';\nimport { ObservablePoint } from '../../maths/point/ObservablePoint';\nimport { uid } from '../../utils/data/uid';\nimport { deprecation, v8_0_0 } from '../../utils/logging/deprecation';\nimport { BigPool } from '../../utils/pool/PoolGroup';\nimport { type IRenderLayer } from '../layers/RenderLayer';\nimport { cacheAsTextureMixin } from './container-mixins/cacheAsTextureMixin';\nimport { childrenHelperMixin } from './container-mixins/childrenHelperMixin';\nimport { collectRenderablesMixin } from './container-mixins/collectRenderablesMixin';\nimport { effectsMixin } from './container-mixins/effectsMixin';\nimport { findMixin } from './container-mixins/findMixin';\nimport { getFastGlobalBoundsMixin } from './container-mixins/getFastGlobalBoundsMixin';\nimport { bgr2rgb, getGlobalMixin } from './container-mixins/getGlobalMixin';\nimport { measureMixin } from './container-mixins/measureMixin';\nimport { onRenderMixin } from './container-mixins/onRenderMixin';\nimport { sortMixin } from './container-mixins/sortMixin';\nimport { toLocalGlobalMixin } from './container-mixins/toLocalGlobalMixin';\nimport { RenderGroup } from './RenderGroup';\nimport { assignWithIgnore } from './utils/assignWithIgnore';\n\nimport type { Size } from '../../maths/misc/Size';\nimport type { PointData } from '../../maths/point/PointData';\nimport type { Rectangle } from '../../maths/shapes/Rectangle';\nimport type { BLEND_MODES } from '../../rendering/renderers/shared/state/const';\nimport type { Dict } from '../../utils/types';\nimport type { Optional } from './container-mixins/measureMixin';\nimport type { DestroyOptions } from './destroyTypes';\n\n/**\n * The type of child that can be added to a {@link Container}.\n * This is a generic type that extends the {@link Container} class.\n * @category scene\n * @standard\n */\nexport type ContainerChild = Container;\n\n// as pivot and skew are the least used properties of a container, we can use this optimisation\n// to avoid allocating lots of unnecessary objects for them.\nconst defaultSkew = new ObservablePoint(null);\nconst defaultPivot = new ObservablePoint(null);\nconst defaultScale = new ObservablePoint(null, 1, 1);\n\n/**\n * Events that can be emitted by a Container. These events provide lifecycle hooks and notifications\n * for container state changes.\n * @example\n * ```ts\n * import { Container, Sprite } from 'pixi.js';\n *\n * // Setup container with event listeners\n * const container = new Container();\n *\n * // Listen for child additions\n * container.on('childAdded', (child, container, index) => {\n * console.log(`Child added at index ${index}:`, child);\n * });\n *\n * // Listen for child removals\n * container.on('childRemoved', (child, container, index) => {\n * console.log(`Child removed from index ${index}:`, child);\n * });\n *\n * // Listen for when container is added to parent\n * container.on('added', (parent) => {\n * console.log('Added to parent:', parent);\n * });\n *\n * // Listen for when container is removed from parent\n * container.on('removed', (parent) => {\n * console.log('Removed from parent:', parent);\n * });\n *\n * // Listen for container destruction\n * container.on('destroyed', (container) => {\n * console.log('Container destroyed:', container);\n * });\n * ```\n * @category scene\n * @standard\n */\nexport interface ContainerEvents<C extends ContainerChild> extends PixiMixins.ContainerEvents\n{\n /**\n * Emitted when this container is added to a new container.\n * Useful for setting up parent-specific behaviors.\n * @param container - The parent container this was added to\n * @example\n * ```ts\n * const child = new Container();\n * child.on('added', (parent) => {\n * console.log('Child added to parent:', parent.label);\n * });\n * parentContainer.addChild(child);\n * ```\n */\n added: [container: Container];\n\n /**\n * Emitted when a child is added to this container.\n * Useful for tracking container composition changes.\n * @param child - The child that was added\n * @param container - The container the child was added to (this container)\n * @param index - The index at which the child was added\n * @example\n * ```ts\n * const parent = new Container();\n * parent.on('childAdded', (child, container, index) => {\n * console.log(`New child at index ${index}:`, child);\n * });\n * ```\n */\n childAdded: [child: C, container: Container, index: number];\n\n /**\n * Emitted when this container is removed from its parent.\n * Useful for cleanup and state management.\n * @param container - The parent container this was removed from\n * @example\n * ```ts\n * const child = new Container();\n * child.on('removed', (oldParent) => {\n * console.log('Child removed from parent:', oldParent.label);\n * });\n * ```\n */\n removed: [container: Container];\n\n /**\n * Emitted when a child is removed from this container.\n * Useful for cleanup and maintaining container state.\n * @param child - The child that was removed\n * @param container - The container the child was removed from (this container)\n * @param index - The index from which the child was removed\n * @example\n * ```ts\n * const parent = new Container();\n * parent.on('childRemoved', (child, container, index) => {\n * console.log(`Child removed from index ${index}:`, child);\n * });\n * ```\n */\n childRemoved: [child: C, container: Container, index: number];\n\n /**\n * Emitted when the container is destroyed.\n * Useful for final cleanup and resource management.\n * @param container - The container that was destroyed\n * @example\n * ```ts\n * const container = new Container();\n * container.on('destroyed', (container) => {\n * console.log('Container destroyed:', container.label);\n * });\n * ```\n */\n destroyed: [container: Container];\n}\n\ntype AnyEvent = {\n // The following is a hack to allow any custom event while maintaining type safety.\n // For some reason, the tsc compiler gets angry about error TS1023\n // \"An index signature parameter type must be either 'string' or 'number'.\"\n // This is really odd since ({}&string) should interpret as string, but then again\n // there is some black magic behind why this works in the first place.\n // Closest thing to an explanation:\n // https://stackoverflow.com/questions/70144348/why-does-a-union-of-type-literals-and-string-cause-ide-code-completion-wh\n //\n // Side note, we disable @typescript-eslint/ban-types since {}&string is the only syntax that works.\n // Nor of the Record/unknown/never alternatives work.\n [K: ({} & string) | ({} & symbol)]: any;\n};\n\n/** @internal */\nexport const UPDATE_COLOR = 0b0001;\n/** @internal */\nexport const UPDATE_BLEND = 0b0010;\n/** @internal */\nexport const UPDATE_VISIBLE = 0b0100;\n/** @internal */\nexport const UPDATE_TRANSFORM = 0b1000;\n\n/**\n * Options for updating the transform of a container.\n * @category scene\n * @standard\n */\nexport interface UpdateTransformOptions\n{\n x: number;\n y: number;\n scaleX: number;\n scaleY: number;\n rotation: number;\n skewX: number;\n skewY: number;\n pivotX: number;\n pivotY: number;\n}\n\n/**\n * Constructor options used for `Container` instances.\n * ```js\n * const container = new Container({\n * position: new Point(100, 200),\n * scale: new Point(2, 2),\n * rotation: Math.PI / 2,\n * });\n * ```\n * @category scene\n * @standard\n * @see Container\n */\nexport interface ContainerOptions<C extends ContainerChild = ContainerChild> extends PixiMixins.ContainerOptions\n{\n /** @see Container#isRenderGroup */\n isRenderGroup?: boolean;\n\n /**\n * The blend mode to be applied to the sprite. Controls how pixels are blended when rendering.\n *\n * Setting to 'normal' will reset to default blending.\n * > [!NOTE] More blend modes are available after importing the `pixi.js/advanced-blend-modes` sub-export.\n * @example\n * ```ts\n * // Basic blend modes\n * new Container({ blendMode: 'normal' }); // Default blending\n * new Container({ blendMode: 'add' }); // Additive blending\n * new Container({ blendMode: 'multiply' }); // Multiply colors\n * new Container({ blendMode: 'screen' }); // Screen blend\n * ```\n * @default 'normal'\n * @see {@link Container#alpha} For transparency\n * @see {@link Container#tint} For color adjustments\n */\n blendMode?: BLEND_MODES;\n /**\n * The tint applied to the sprite.\n *\n * This can be any valid {@link ColorSource}.\n * @example\n * ```ts\n * new Container({ tint: 0xff0000 }); // Red tint\n * new Container({ tint: 'blue' }); // Blue tint\n * new Container({ tint: '#00ff00' }); // Green tint\n * new Container({ tint: 'rgb(0,0,255)' }); // Blue tint\n * ```\n * @default 0xFFFFFF\n * @see {@link Container#alpha} For transparency\n * @see {@link Container#visible} For visibility control\n */\n tint?: ColorSource;\n\n /**\n * The opacity of the object relative to its parent's opacity.\n * Value ranges from 0 (fully transparent) to 1 (fully opaque).\n * @example\n * ```ts\n * new Container({ alpha: 0.5 }); // 50% opacity\n * new Container({ alpha: 1 }); // Fully opaque\n * ```\n * @default 1\n * @see {@link Container#visible} For toggling visibility\n * @see {@link Container#renderable} For render control\n */\n alpha?: number;\n /**\n * The angle of the object in degrees.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n @example\n * ```ts\n * new Container({ angle: 45 }); // Rotate 45 degrees\n * new Container({ angle: 90 }); // Rotate 90 degrees\n * ```\n */\n angle?: number;\n /**\n * The array of children of this container. Each child must be a Container or extend from it.\n *\n * The array is read-only, but its contents can be modified using Container methods.\n * @example\n * ```ts\n * new Container({\n * children: [\n * new Container(), // First child\n * new Container(), // Second child\n * ],\n * });\n * ```\n * @readonly\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChild} For removing children\n */\n children?: C[];\n /**\n * The display object container that contains this display object.\n * This represents the parent-child relationship in the display tree.\n * @readonly\n * @see {@link Container#addChild} For adding to a parent\n * @see {@link Container#removeChild} For removing from parent\n */\n parent?: Container;\n /**\n * Controls whether this object can be rendered. If false the object will not be drawn,\n * but the transform will still be updated. This is different from visible, which skips\n * transform updates.\n * @example\n * ```ts\n * new Container({ renderable: false }); // Will not be drawn, but transforms will update\n * ```\n * @default true\n * @see {@link Container#visible} For skipping transform updates\n * @see {@link Container#alpha} For transparency\n */\n renderable?: boolean;\n /**\n * The rotation of the object in radians.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n * @example\n * ```ts\n * new Container({ rotation: Math.PI / 4 }); // Rotate 45 degrees\n * new Container({ rotation: Math.PI / 2 }); // Rotate 90 degrees\n * ```\n */\n rotation?: number;\n /**\n * The scale factors of this object along the local coordinate axes.\n *\n * The default scale is (1, 1).\n * @example\n * ```ts\n * new Container({ scale: new Point(2, 2) }); // Scale by 2x\n * new Container({ scale: 0.5 }); // Scale by 0.5x\n * new Container({ scale: { x: 1.5, y: 1.5 } }); // Scale by 1.5x\n * ```\n */\n scale?: PointData | number;\n /**\n * The center of rotation, scaling, and skewing for this display object in its local space.\n * The `position` is the projection of `pivot` in the parent's local space.\n *\n * By default, the pivot is the origin (0, 0).\n * @example\n * ```ts\n * new Container({ pivot: new Point(100, 200) }); // Set pivot to (100, 200)\n * new Container({ pivot: 50 }); // Set pivot to (50, 50)\n * new Container({ pivot: { x: 150, y: 150 } }); // Set pivot to (150, 150)\n * ```\n */\n pivot?: PointData | number;\n /**\n * The coordinate of the object relative to the local coordinates of the parent.\n * @example\n * ```ts\n * new Container({ position: new Point(100, 200) }); // Set position to (100, 200)\n * new Container({ position: { x: 150, y: 150 } }); // Set position to (150, 150)\n * ```\n */\n position?: PointData;\n /**\n * The skew factor for the object in radians. Skewing is a transformation that distorts\n * the object by rotating it differently at each point, creating a non-uniform shape.\n * @example\n * ```ts\n * new Container({ skew: new Point(0.1, 0.2) }); // Skew by 0.1 radians on x and 0.2 radians on y\n * new Container({ skew: { x: 0.1, y: 0.2 } }); // Skew by 0.1 radians on x and 0.2 radians on y\n * ```\n * @default { x: 0, y: 0 }\n */\n skew?: PointData;\n /**\n * The visibility of the object. If false the object will not be drawn,\n * and the transform will not be updated.\n * @example\n * ```ts\n * new Container({ visible: false }); // Will not be drawn and transforms will not update\n * new Container({ visible: true }); // Will be drawn and transforms will update\n * ```\n * @default true\n * @see {@link Container#renderable} For render-only control\n * @see {@link Container#alpha} For transparency\n */\n visible?: boolean;\n /**\n * The position of the container on the x axis relative to the local coordinates of the parent.\n *\n * An alias to position.x\n * @example\n * ```ts\n * new Container({ x: 100 }); // Set x position to 100\n * ```\n */\n x?: number;\n /**\n * The position of the container on the y axis relative to the local coordinates of the parent.\n *\n * An alias to position.y\n * @example\n * ```ts\n * new Container({ y: 200 }); // Set y position to 200\n * ```\n */\n y?: number;\n /**\n * An optional bounds area for this container. Setting this rectangle will stop the renderer\n * from recursively measuring the bounds of each children and instead use this single boundArea.\n *\n * > [!IMPORTANT] This is great for optimisation! If for example you have a\n * > 1000 spinning particles and you know they all sit within a specific bounds,\n * > then setting it will mean the renderer will not need to measure the\n * > 1000 children to find the bounds. Instead it will just use the bounds you set.\n * @example\n * ```ts\n * const container = new Container({\n * boundsArea: new Rectangle(0, 0, 500, 500) // Set a fixed bounds area\n * });\n * ```\n */\n boundsArea?: Rectangle;\n}\n\n// eslint-disable-next-line requireExport/require-export-jsdoc, requireMemberAPI/require-member-api-doc\nexport interface Container<C extends ContainerChild>\n extends PixiMixins.Container<C>, EventEmitter<ContainerEvents<C> & AnyEvent> {}\n\n/**\n * Container is a general-purpose display object that holds children. It also adds built-in support for advanced\n * rendering features like masking and filtering.\n *\n * It is the base class of all display objects that act as a container for other objects, including Graphics\n * and Sprite.\n *\n * <details id=\"transforms\">\n *\n * <summary>Transforms</summary>\n *\n * The [transform]{@link Container#localTransform} of a display object describes the projection from its\n * local coordinate space to its parent's local coordinate space. The following properties are derived\n * from the transform:\n *\n * <table>\n * <thead>\n * <tr>\n * <th>Property</th>\n * <th>Description</th>\n * </tr>\n * </thead>\n * <tbody>\n * <tr>\n * <td>[pivot]{@link Container#pivot}</td>\n * <td>\n * Invariant under rotation, scaling, and skewing. The projection of into the parent's space of the pivot\n * is equal to position, regardless of the other three transformations. In other words, It is the center of\n * rotation, scaling, and skewing.\n * </td>\n * </tr>\n * <tr>\n * <td>[position]{@link Container#position}</td>\n * <td>\n * Translation. This is the position of the [pivot]{@link Container#pivot} in the parent's local\n * space. The default value of the pivot is the origin (0,0). If the top-left corner of your display object\n * is (0,0) in its local space, then the position will be its top-left corner in the parent's local space.\n * </td>\n * </tr>\n * <tr>\n * <td>[scale]{@link Container#scale}</td>\n * <td>\n * Scaling. This will stretch (or compress) the display object's projection. The scale factors are along the\n * local coordinate axes. In other words, the display object is scaled before rotated or skewed. The center\n * of scaling is the [pivot]{@link Container#pivot}.\n * </td>\n * </tr>\n * <tr>\n * <td>[rotation]{@link Container#rotation}</td>\n * <td>\n * Rotation. This will rotate the display object's projection by this angle (in radians).\n * </td>\n * </tr>\n * <tr>\n * <td>[skew]{@link Container#skew}</td>\n * <td>\n * <p>Skewing. This can be used to deform a rectangular display object into a parallelogram.</p>\n * <p>\n * In PixiJS, skew has a slightly different behaviour than the conventional meaning. It can be\n * thought of the net rotation applied to the coordinate axes (separately). For example, if \"skew.x\" is\n * ⍺ and \"skew.y\" is β, then the line x = 0 will be rotated by ⍺ (y = -x*cot⍺) and the line y = 0 will be\n * rotated by β (y = x*tanβ). A line y = x*tanϴ (i.e. a line at angle ϴ to the x-axis in local-space) will\n * be rotated by an angle between ⍺ and β.\n * </p>\n * <p>\n * It can be observed that if skew is applied equally to both axes, then it will be equivalent to applying\n * a rotation. Indeed, if \"skew.x\" = -ϴ and \"skew.y\" = ϴ, it will produce an equivalent of \"rotation\" = ϴ.\n * </p>\n * <p>\n * Another quite interesting observation is that \"skew.x\", \"skew.y\", rotation are commutative operations. Indeed,\n * because rotation is essentially a careful combination of the two.\n * </p>\n * </td>\n * </tr>\n * <tr>\n * <td>[angle]{@link Container#angle}</td>\n * <td>Rotation. This is an alias for [rotation]{@link Container#rotation}, but in degrees.</td>\n * </tr>\n * <tr>\n * <td>[x]{@link Container#x}</td>\n * <td>Translation. This is an alias for position.x!</td>\n * </tr>\n * <tr>\n * <td>[y]{@link Container#y}</td>\n * <td>Translation. This is an alias for position.y!</td>\n * </tr>\n * <tr>\n * <td>[width]{@link Container#width}</td>\n * <td>\n * Implemented in [Container]{@link Container}. Scaling. The width property calculates scale.x by dividing\n * the \"requested\" width by the local bounding box width. It is indirectly an abstraction over scale.x, and there\n * is no concept of user-defined width.\n * </td>\n * </tr>\n * <tr>\n * <td>[height]{@link Container#height}</td>\n * <td>\n * Implemented in [Container]{@link Container}. Scaling. The height property calculates scale.y by dividing\n * the \"requested\" height by the local bounding box height. It is indirectly an abstraction over scale.y, and there\n * is no concept of user-defined height.\n * </td>\n * </tr>\n * </tbody>\n * </table>\n * </details>\n *\n * <details id=\"alpha\">\n * <summary>Alpha</summary>\n *\n * This alpha sets a display object's **relative opacity** w.r.t its parent. For example, if the alpha of a display\n * object is 0.5 and its parent's alpha is 0.5, then it will be rendered with 25% opacity (assuming alpha is not\n * applied on any ancestor further up the chain).\n * </details>\n *\n * <details id=\"visible\">\n * <summary>Renderable vs Visible</summary>\n *\n * The `renderable` and `visible` properties can be used to prevent a display object from being rendered to the\n * screen. However, there is a subtle difference between the two. When using `renderable`, the transforms of the display\n * object (and its children subtree) will continue to be calculated. When using `visible`, the transforms will not\n * be calculated.\n * ```ts\n * import { BlurFilter, Container, Graphics, Sprite } from 'pixi.js';\n *\n * const container = new Container();\n * const sprite = Sprite.from('https://s3-us-west-2.amazonaws.com/s.cdpn.io/693612/IaUrttj.png');\n *\n * sprite.width = 512;\n * sprite.height = 512;\n *\n * // Adds a sprite as a child to this container. As a result, the sprite will be rendered whenever the container\n * // is rendered.\n * container.addChild(sprite);\n *\n * // Blurs whatever is rendered by the container\n * container.filters = [new BlurFilter()];\n *\n * // Only the contents within a circle at the center should be rendered onto the screen.\n * container.mask = new Graphics()\n * .beginFill(0xffffff)\n * .drawCircle(sprite.width / 2, sprite.height / 2, Math.min(sprite.width, sprite.height) / 2)\n * .endFill();\n * ```\n *\n * </details>\n *\n * <details id=\"renderGroup\">\n * <summary>RenderGroup</summary>\n *\n * In PixiJS v8, containers can be set to operate in 'render group mode',\n * transforming them into entities akin to a stage in traditional rendering paradigms.\n * A render group is a root renderable entity, similar to a container,\n * but it's rendered in a separate pass with its own unique set of rendering instructions.\n * This approach enhances rendering efficiency and organization, particularly in complex scenes.\n *\n * You can enable render group mode on any container using container.enableRenderGroup()\n * or by initializing a new container with the render group property set to true (new Container({isRenderGroup: true})).\n * The method you choose depends on your specific use case and setup requirements.\n *\n * An important aspect of PixiJS’s rendering process is the automatic treatment of rendered scenes as render groups.\n * This conversion streamlines the rendering process, but understanding when and how this happens is crucial\n * to fully leverage its benefits.\n *\n * One of the key advantages of using render groups is the performance efficiency in moving them. Since transformations\n * are applied at the GPU level, moving a render group, even one with complex and numerous children,\n * doesn't require recalculating the rendering instructions or performing transformations on each child.\n * This makes operations like panning a large game world incredibly efficient.\n *\n * However, it's crucial to note that render groups do not batch together.\n * This means that turning every container into a render group could actually slow things down,\n * as each render group is processed separately. It's best to use render groups judiciously, at a broader level,\n * rather than on a per-child basis.\n * This approach ensures you get the performance benefits without overburdening the rendering process.\n *\n * RenderGroups maintain their own set of rendering instructions,\n * ensuring that changes or updates within a render group don't affect the rendering\n * instructions of its parent or other render groups.\n * This isolation ensures more stable and predictable rendering behavior.\n *\n * Additionally, renderGroups can be nested, allowing for powerful options in organizing different aspects of your scene.\n * This feature is particularly beneficial for separating complex game graphics from UI elements,\n * enabling intricate and efficient scene management in complex applications.\n *\n * This means that Containers have 3 levels of matrix to be mindful of:\n *\n * 1. localTransform, this is the transform of the container based on its own properties\n * 2. groupTransform, this it the transform of the container relative to the renderGroup it belongs too\n * 3. worldTransform, this is the transform of the container relative to the Scene being rendered\n * </details>\n * @category scene\n * @standard\n */\nexport class Container<C extends ContainerChild = ContainerChild> extends EventEmitter<ContainerEvents<C> & AnyEvent>\n{\n /**\n * Mixes all enumerable properties and methods from a source object to Container.\n * @param source - The source of properties and methods to mix in.\n * @deprecated since 8.8.0\n */\n public static mixin(source: Dict<any>): void\n {\n // #if _DEBUG\n deprecation('8.8.0', 'Container.mixin is deprecated, please use extensions.mixin instead.');\n // #endif\n extensions.mixin(Container, source);\n }\n\n /**\n * unique id for this container\n * @internal\n */\n public readonly uid: number = uid('renderable');\n\n /** @private */\n public _updateFlags = 0b1111;\n\n // the render group this container owns\n /** @private */\n public renderGroup: RenderGroup = null;\n // the render group this container belongs to\n /** @private */\n public parentRenderGroup: RenderGroup = null;\n // the index of the container in the render group\n /** @private */\n public parentRenderGroupIndex: number = 0;\n\n // set to true if the container has changed. It is reset once the changes have been applied\n // by the transform system\n // its here to stop ensure that when things change, only one update gets registers with the transform system\n /** @private */\n public didChange = false;\n // same as above, but for the renderable\n /** @private */\n public didViewUpdate = false;\n\n // how deep is the container relative to its render group..\n // unless the element is the root render group - it will be relative to its parent\n /** @private */\n public relativeRenderGroupDepth = 0;\n\n /**\n * The array of children of this container. Each child must be a Container or extend from it.\n *\n * The array is read-only, but its contents can be modified using Container methods.\n * @example\n * ```ts\n * // Access children\n * const firstChild = container.children[0];\n * const lastChild = container.children[container.children.length - 1];\n * ```\n * @readonly\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChild} For removing children\n */\n public children: C[] = [];\n /**\n * The display object container that contains this display object.\n * This represents the parent-child relationship in the display tree.\n * @example\n * ```ts\n * // Basic parent access\n * const parent = sprite.parent;\n *\n * // Walk up the tree\n * let current = sprite;\n * while (current.parent) {\n * console.log('Level up:', current.parent.constructor.name);\n * current = current.parent;\n * }\n * ```\n * @readonly\n * @see {@link Container#addChild} For adding to a parent\n * @see {@link Container#removeChild} For removing from parent\n */\n public parent: Container = null;\n\n // used internally for changing up the render order.. mainly for masks and filters\n // TODO setting this should cause a rebuild??\n /** @private */\n public includeInBuild = true;\n /** @private */\n public measurable = true;\n /** @private */\n public isSimple = true;\n\n /**\n * The RenderLayer this container belongs to, if any.\n * If it belongs to a RenderLayer, it will be rendered from the RenderLayer's position in the scene.\n * @readonly\n * @advanced\n */\n public parentRenderLayer: IRenderLayer;\n\n // / /////////////Transform related props//////////////\n\n // used by the transform system to check if a container needs to be updated that frame\n // if the tick matches the current transform system tick, it is not updated again\n /** @internal */\n public updateTick = -1;\n\n /**\n * Current transform of the object based on local factors: position, scale, other stuff.\n * This matrix represents the local transformation without any parent influence.\n * @example\n * ```ts\n * // Basic transform access\n * const localMatrix = sprite.localTransform;\n * console.log(localMatrix.toString());\n * ```\n * @readonly\n * @see {@link Container#worldTransform} For global transform\n * @see {@link Container#groupTransform} For render group transform\n */\n public localTransform: Matrix = new Matrix();\n /**\n * The relative group transform is a transform relative to the render group it belongs too. It will include all parent\n * transforms and up to the render group (think of it as kind of like a stage - but the stage can be nested).\n * If this container is is self a render group matrix will be relative to its parent render group\n * @readonly\n * @advanced\n */\n public relativeGroupTransform: Matrix = new Matrix();\n /**\n * The group transform is a transform relative to the render group it belongs too.\n * If this container is render group then this will be an identity matrix. other wise it\n * will be the same as the relativeGroupTransform.\n * Use this value when actually rendering things to the screen\n * @readonly\n * @advanced\n */\n public groupTransform: Matrix = this.relativeGroupTransform;\n\n // the global transform taking into account the render group and all parents\n private _worldTransform: Matrix;\n\n /**\n * Whether this object has been destroyed. If true, the object should no longer be used.\n * After an object is destroyed, all of its functionality is disabled and references are removed.\n * @example\n * ```ts\n * // Cleanup with destroy\n * sprite.destroy();\n * console.log(sprite.destroyed); // true\n * ```\n * @default false\n * @see {@link Container#destroy} For destroying objects\n */\n public destroyed = false;\n\n // transform data..\n /**\n * The coordinate of the object relative to the local coordinates of the parent.\n * @internal\n */\n public _position: ObservablePoint = new ObservablePoint(this, 0, 0);\n\n /**\n * The scale factor of the object.\n * @internal\n */\n public _scale: ObservablePoint = defaultScale;\n\n /**\n * The pivot point of the container that it rotates around.\n * @internal\n */\n public _pivot: ObservablePoint = defaultPivot;\n\n /**\n * The skew amount, on the x and y axis.\n * @internal\n */\n public _skew: ObservablePoint = defaultSkew;\n\n /**\n * The X-coordinate value of the normalized local X axis,\n * the first column of the local transformation matrix without a scale.\n * @internal\n */\n public _cx = 1;\n\n /**\n * The Y-coordinate value of the normalized local X axis,\n * the first column of the local transformation matrix without a scale.\n * @internal\n */\n public _sx = 0;\n\n /**\n * The X-coordinate value of the normalized local Y axis,\n * the second column of the local transformation matrix without a scale.\n * @internal\n */\n public _cy = 0;\n\n /**\n * The Y-coordinate value of the normalized local Y axis,\n * the second column of the local transformation matrix without a scale.\n * @internal\n */\n public _sy = 1;\n\n /**\n * The rotation amount.\n * @internal\n */\n private _rotation = 0;\n\n // / COLOR related props //////////////\n\n // color stored as ABGR\n /** @internal */\n public localColor = 0xFFFFFF;\n /** @internal */\n public localAlpha = 1;\n\n /** @internal */\n public groupAlpha = 1; // A\n /** @internal */\n public groupColor = 0xFFFFFF; // BGR\n /** @internal */\n public groupColorAlpha = 0xFFFFFFFF; // ABGR\n\n // / BLEND related props //////////////\n\n /** @internal */\n public localBlendMode: BLEND_MODES = 'inherit';\n /** @internal */\n public groupBlendMode: BLEND_MODES = 'normal';\n\n // / VISIBILITY related props //////////////\n\n // visibility\n // 0b11\n // first bit is visible, second bit is renderable\n /**\n * This property holds three bits: culled, visible, renderable\n * the third bit represents culling (0 = culled, 1 = not culled) 0b100\n * the second bit represents visibility (0 = not visible, 1 = visible) 0b010\n * the first bit represents renderable (0 = not renderable, 1 = renderable) 0b001\n * @internal\n */\n public localDisplayStatus = 0b111; // 0b11 | 0b10 | 0b01 | 0b00\n /** @internal */\n public globalDisplayStatus = 0b111; // 0b11 | 0b10 | 0b01 | 0b00\n\n /** @internal */\n public readonly renderPipeId: string;\n\n /**\n * An optional bounds area for this container. Setting this rectangle will stop the renderer\n * from recursively measuring the bounds of each children and instead use this single boundArea.\n *\n * > [!IMPORTANT] This is great for optimisation! If for example you have a\n * > 1000 spinning particles and you know they all sit within a specific bounds,\n * > then setting it will mean the renderer will not need to measure the\n * > 1000 children to find the bounds. Instead it will just use the bounds you set.\n * @example\n * ```ts\n * const container = new Container();\n * container.boundsArea = new Rectangle(0, 0, 500, 500);\n * ```\n */\n public boundsArea: Rectangle;\n\n /**\n * A value that increments each time the containe is modified\n * eg children added, removed etc\n * @ignore\n */\n public _didContainerChangeTick = 0;\n /**\n * A value that increments each time the container view is modified\n * eg texture swap, geometry change etc\n * @ignore\n */\n public _didViewChangeTick = 0;\n\n /** @internal */\n public layerParentId: string;// = 'default';\n /**\n * We now use the _didContainerChangeTick and _didViewChangeTick to track changes\n * @deprecated since 8.2.6\n * @ignore\n */\n set _didChangeId(value: number)\n {\n this._didViewChangeTick = (value >> 12) & 0xFFF; // Extract the upper 12 bits\n this._didContainerChangeTick = value & 0xFFF; // Extract the lower 12 bits\n }\n /** @ignore */\n get _didChangeId(): number\n {\n return (this._didContainerChangeTick & 0xfff) | ((this._didViewChangeTick & 0xfff) << 12);\n }\n\n /**\n * property that tracks if the container transform has changed\n * @ignore\n */\n private _didLocalTransformChangeId = -1;\n\n constructor(options: ContainerOptions<C> = {})\n {\n super();\n\n this.effects = [];\n assignWithIgnore(this, options, {\n children: true,\n parent: true,\n effects: true,\n });\n\n options.children?.forEach((child) => this.addChild(child));\n options.parent?.addChild(this);\n }\n\n /**\n * Adds one or more children to the container.\n * The children will be rendered as part of this container's display list.\n * @example\n * ```ts\n * // Add a single child\n * container.addChild(sprite);\n *\n * // Add multiple children\n * container.addChild(background, player, foreground);\n *\n * // Add with type checking\n * const sprite = container.addChild<Sprite>(new Sprite(texture));\n * sprite.tint = 'red';\n * ```\n * @param children - The Container(s) to add to the container\n * @returns The first child that was added\n * @see {@link Container#removeChild} For removing children\n * @see {@link Container#addChildAt} For adding at specific index\n */\n public addChild<U extends(C | IRenderLayer)[]>(...children: U): U[0]\n {\n // #if _DEBUG\n if (!this.allowChildren)\n {\n deprecation(v8_0_0, 'addChild: Only Containers will be allowed to add children in v8.0.0');\n }\n // #endif\n\n if (children.length > 1)\n {\n // loop through the array and add all children\n for (let i = 0; i < children.length; i++)\n {\n this.addChild(children[i]);\n }\n\n return children[0];\n }\n\n const child = children[0] as C;\n\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (child.parent === this)\n {\n this.children.splice(this.children.indexOf(child), 1);\n this.children.push(child);\n\n if (renderGroup)\n {\n renderGroup.structureDidChange = true;\n }\n\n return child;\n }\n\n if (child.parent)\n {\n // TODO Optimisation...if the parent has the same render group, this does not need to change!\n child.parent.removeChild(child);\n }\n\n this.children.push(child);\n\n if (this.sortableChildren) this.sortDirty = true;\n\n child.parent = this;\n\n child.didChange = true;\n\n // TODO - OPtimise this? could check what the parent has set?\n child._updateFlags = 0b1111;\n\n if (renderGroup)\n {\n renderGroup.addChild(child);\n }\n\n this.emit('childAdded', child, this, this.children.length - 1);\n child.emit('added', this);\n\n this._didViewChangeTick++;\n\n if (child._zIndex !== 0)\n {\n child.depthOfChildModified();\n }\n\n return child;\n }\n\n /**\n * Removes one or more children from the container.\n * When removing multiple children, events will be triggered for each child in sequence.\n * @example\n * ```ts\n * // Remove a single child\n * const removed = container.removeChild(sprite);\n *\n * // Remove multiple children\n * const bg = container.removeChild(background, player, userInterface);\n *\n * // Remove with type checking\n * const sprite = container.removeChild<Sprite>(childSprite);\n * sprite.texture = newTexture;\n * ```\n * @param children - The Container(s) to remove\n * @returns The first child that was removed\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChildren} For removing multiple children\n */\n public removeChild<U extends(C | IRenderLayer)[]>(...children: U): U[0]\n {\n // if there is only one argument we can bypass looping through the them\n if (children.length > 1)\n {\n // loop through the arguments property and remove all children\n for (let i = 0; i < children.length; i++)\n {\n this.removeChild(children[i]);\n }\n\n return children[0];\n }\n\n const child = children[0] as C;\n\n const index = this.children.indexOf(child);\n\n if (index > -1)\n {\n this._didViewChangeTick++;\n\n this.children.splice(index, 1);\n\n if (this.renderGroup)\n {\n this.renderGroup.removeChild(child);\n }\n else if (this.parentRenderGroup)\n {\n this.parentRenderGroup.removeChild(child);\n }\n\n if (child.parentRenderLayer)\n {\n child.parentRenderLayer.detach(child);\n }\n\n child.parent = null;\n this.emit('childRemoved', child, this, index);\n child.emit('removed', this);\n }\n\n return child;\n }\n\n /** @ignore */\n public _onUpdate(point?: ObservablePoint)\n {\n if (point)\n {\n // this.updateFlags |= UPDATE_TRANSFORM;\n\n if (point === this._skew)\n {\n this._updateSkew();\n }\n }\n\n this._didContainerChangeTick++;\n\n if (this.didChange) return;\n this.didChange = true;\n\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.onChildUpdate(this);\n }\n }\n\n set isRenderGroup(value: boolean)\n {\n if (!!this.renderGroup === value) return;\n\n if (value)\n {\n this.enableRenderGroup();\n }\n else\n {\n this.disableRenderGroup();\n }\n }\n\n /**\n * Returns true if this container is a render group.\n * This means that it will be rendered as a separate pass, with its own set of instructions\n * @advanced\n */\n get isRenderGroup(): boolean\n {\n return !!this.renderGroup;\n }\n\n /**\n * Calling this enables a render group for this container.\n * This means it will be rendered as a separate set of instructions.\n * The transform of the container will also be handled on the GPU rather than the CPU.\n * @advanced\n */\n public enableRenderGroup(): void\n {\n if (this.renderGroup) return;\n\n const parentRenderGroup = this.parentRenderGroup;\n\n parentRenderGroup?.removeChild(this);\n\n this.renderGroup = BigPool.get(RenderGroup, this);\n\n // this group matrix will now be an identity matrix,\n // as its own transform will be passed to the GPU\n this.groupTransform = Matrix.IDENTITY;\n\n parentRenderGroup?.addChild(this);\n\n this._updateIsSimple();\n }\n\n /**\n * This will disable the render group for this container.\n * @advanced\n */\n public disableRenderGroup(): void\n {\n if (!this.renderGroup) return;\n\n const parentRenderGroup = this.parentRenderGroup;\n\n parentRenderGroup?.removeChild(this);\n\n BigPool.return(this.renderGroup);\n\n this.renderGroup = null;\n this.groupTransform = this.relativeGroupTransform;\n\n parentRenderGroup?.addChild(this);\n\n this._updateIsSimple();\n }\n\n /** @ignore */\n public _updateIsSimple()\n {\n this.isSimple = !(this.renderGroup) && (this.effects.length === 0);\n }\n\n /**\n * Current transform of the object based on world (parent) factors.\n *\n * This matrix represents the absolute transformation in the scene graph.\n * @example\n * ```ts\n * // Get world position\n * const worldPos = container.worldTransform;\n * console.log(`World position: (${worldPos.tx}, ${worldPos.ty})`);\n * ```\n * @readonly\n * @see {@link Container#localTransform} For local space transform\n */\n get worldTransform()\n {\n this._worldTransform ||= new Matrix();\n\n if (this.renderGroup)\n {\n this._worldTransform.copyFrom(this.renderGroup.worldTransform);\n }\n else if (this.parentRenderGroup)\n {\n this._worldTransform.appendFrom(this.relativeGroupTransform, this.parentRenderGroup.worldTransform);\n }\n\n return this._worldTransform;\n }\n\n /**\n * The position of the container on the x axis relative to the local coordinates of the parent.\n *\n * An alias to position.x\n * @example\n * ```ts\n * // Basic position\n * container.x = 100;\n * ```\n */\n get x(): number\n {\n return this._position.x;\n }\n\n set x(value: number)\n {\n this._position.x = value;\n }\n\n /**\n * The position of the container on the y axis relative to the local coordinates of the parent.\n *\n * An alias to position.y\n * @example\n * ```ts\n * // Basic position\n * container.y = 200;\n * ```\n */\n get y(): number\n {\n return this._position.y;\n }\n\n set y(value: number)\n {\n this._position.y = value;\n }\n\n /**\n * The coordinate of the object relative to the local coordinates of the parent.\n * @example\n * ```ts\n * // Basic position setting\n * container.position.set(100, 200);\n * container.position.set(100); // Sets both x and y to 100\n * // Using point data\n * container.position = { x: 50, y: 75 };\n * ```\n * @since 4.0.0\n */\n get position(): ObservablePoint\n {\n return this._position;\n }\n\n set position(value: PointData)\n {\n this._position.copyFrom(value);\n }\n\n /**\n * The rotation of the object in radians.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n * @example\n * ```ts\n * // Basic rotation\n * container.rotation = Math.PI / 4; // 45 degrees\n *\n * // Convert from degrees\n * const degrees = 45;\n * container.rotation = degrees * Math.PI / 180;\n *\n * // Rotate around center\n * container.pivot.set(container.width / 2, container.height / 2);\n * container.rotation = Math.PI; // 180 degrees\n * ```\n */\n get rotation(): number\n {\n return this._rotation;\n }\n\n set rotation(value: number)\n {\n if (this._rotation !== value)\n {\n this._rotation = value;\n this._onUpdate(this._skew);\n }\n }\n\n /**\n * The angle of the object in degrees.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n @example\n * ```ts\n * // Basic angle rotation\n * sprite.angle = 45; // 45 degrees\n *\n * // Rotate around center\n * sprite.pivot.set(sprite.width / 2, sprite.height / 2);\n * sprite.angle = 180; // Half rotation\n *\n * // Reset rotation\n * sprite.angle = 0;\n * ```\n */\n get angle(): number\n {\n return this.rotation * RAD_TO_DEG;\n }\n\n set angle(value: number)\n {\n this.rotation = value * DEG_TO_RAD;\n }\n\n /**\n * The center of rotation, scaling, and skewing for this display object in its local space.\n * The `position` is the projection of `pivot` in the parent's local space.\n *\n * By default, the pivot is the origin (0, 0).\n * @example\n * ```ts\n * // Rotate around center\n * container.pivot.set(container.width / 2, container.height / 2);\n * container.rotation = Math.PI; // Rotates around center\n * ```\n * @since 4.0.0\n */\n get pivot(): ObservablePoint\n {\n if (this._pivot === defaultPivot)\n {\n this._pivot = new ObservablePoint(this, 0, 0);\n }\n\n return this._pivot;\n }\n\n set pivot(value: PointData | number)\n {\n if (this._pivot === defaultPivot)\n {\n this._pivot = new ObservablePoint(this, 0, 0);\n }\n\n typeof value === 'number' ? this._pivot.set(value) : this._pivot.copyFrom(value);\n }\n\n /**\n * The skew factor for the object in radians. Skewing is a transformation that distorts\n * the object by rotating it differently at each point, creating a non-uniform shape.\n * @example\n * ```ts\n * // Basic skewing\n * container.skew.set(0.5, 0); // Skew horizontally\n * container.skew.set(0, 0.5); // Skew vertically\n *\n * // Skew with point data\n * container.skew = { x: 0.3, y: 0.3 }; // Diagonal skew\n *\n * // Reset skew\n * container.skew.set(0, 0);\n *\n * // Animate skew\n * app.ticker.add(() => {\n * // Create wave effect\n * container.skew.x = Math.sin(Date.now() / 1000) * 0.3;\n * });\n *\n * // Combine with rotation\n * container.rotation = Math.PI / 4; // 45 degrees\n * container.skew.set(0.2, 0.2); // Skew the rotated object\n * ```\n * @since 4.0.0\n * @type {ObservablePoint} Point-like object with x/y properties in radians\n * @default {x: 0, y: 0}\n */\n get skew(): ObservablePoint\n {\n if (this._skew === defaultSkew)\n {\n this._skew = new ObservablePoint(this, 0