368 lines
15 KiB
JavaScript
368 lines
15 KiB
JavaScript
import { utils, Transform, Rectangle, RAD_TO_DEG, DEG_TO_RAD } from "@pixi/core";
|
|
import { Bounds } from "./Bounds.mjs";
|
|
class DisplayObject extends utils.EventEmitter {
|
|
constructor() {
|
|
super(), this.tempDisplayObjectParent = null, this.transform = new Transform(), this.alpha = 1, this.visible = !0, this.renderable = !0, this.cullable = !1, this.cullArea = null, this.parent = null, this.worldAlpha = 1, this._lastSortedIndex = 0, this._zIndex = 0, this.filterArea = null, this.filters = null, this._enabledFilters = null, this._bounds = new Bounds(), this._localBounds = null, this._boundsID = 0, this._boundsRect = null, this._localBoundsRect = null, this._mask = null, this._maskRefCount = 0, this._destroyed = !1, this.isSprite = !1, this.isMask = !1;
|
|
}
|
|
/**
|
|
* Mixes all enumerable properties and methods from a source object to DisplayObject.
|
|
* @param source - The source of properties and methods to mix in.
|
|
*/
|
|
static mixin(source) {
|
|
const keys = Object.keys(source);
|
|
for (let i = 0; i < keys.length; ++i) {
|
|
const propertyName = keys[i];
|
|
Object.defineProperty(
|
|
DisplayObject.prototype,
|
|
propertyName,
|
|
Object.getOwnPropertyDescriptor(source, propertyName)
|
|
);
|
|
}
|
|
}
|
|
/**
|
|
* Fired when this DisplayObject is added to a Container.
|
|
* @instance
|
|
* @event added
|
|
* @param {PIXI.Container} container - The container added to.
|
|
*/
|
|
/**
|
|
* Fired when this DisplayObject is removed from a Container.
|
|
* @instance
|
|
* @event removed
|
|
* @param {PIXI.Container} container - The container removed from.
|
|
*/
|
|
/**
|
|
* Fired when this DisplayObject is destroyed. This event is emitted once
|
|
* destroy is finished.
|
|
* @instance
|
|
* @event destroyed
|
|
*/
|
|
/** Readonly flag for destroyed display objects. */
|
|
get destroyed() {
|
|
return this._destroyed;
|
|
}
|
|
/** Recursively updates transform of all objects from the root to this one internal function for toLocal() */
|
|
_recursivePostUpdateTransform() {
|
|
this.parent ? (this.parent._recursivePostUpdateTransform(), this.transform.updateTransform(this.parent.transform)) : this.transform.updateTransform(this._tempDisplayObjectParent.transform);
|
|
}
|
|
/** Updates the object transform for rendering. TODO - Optimization pass! */
|
|
updateTransform() {
|
|
this._boundsID++, this.transform.updateTransform(this.parent.transform), this.worldAlpha = this.alpha * this.parent.worldAlpha;
|
|
}
|
|
/**
|
|
* Calculates and returns the (world) bounds of the display object as a [Rectangle]{@link PIXI.Rectangle}.
|
|
*
|
|
* This method is expensive on containers with a large subtree (like the stage). This is because the bounds
|
|
* of a container depend on its children's bounds, which recursively causes all bounds in the subtree to
|
|
* be recalculated. The upside, however, is that calling `getBounds` once on a container will indeed update
|
|
* the bounds of all children (the whole subtree, in fact). This side effect should be exploited by using
|
|
* `displayObject._bounds.getRectangle()` when traversing through all the bounds in a scene graph. Otherwise,
|
|
* calling `getBounds` on each object in a subtree will cause the total cost to increase quadratically as
|
|
* its height increases.
|
|
*
|
|
* The transforms of all objects in a container's **subtree** and of all **ancestors** are updated.
|
|
* The world bounds of all display objects in a container's **subtree** will also be recalculated.
|
|
*
|
|
* The `_bounds` object stores the last calculation of the bounds. You can use to entirely skip bounds
|
|
* calculation if needed.
|
|
*
|
|
* ```js
|
|
* const lastCalculatedBounds = displayObject._bounds.getRectangle(optionalRect);
|
|
* ```
|
|
*
|
|
* Do know that usage of `getLocalBounds` can corrupt the `_bounds` of children (the whole subtree, actually). This
|
|
* is a known issue that has not been solved. See [getLocalBounds]{@link PIXI.DisplayObject#getLocalBounds} for more
|
|
* details.
|
|
*
|
|
* `getBounds` should be called with `skipUpdate` equal to `true` in a render() call. This is because the transforms
|
|
* are guaranteed to be update-to-date. In fact, recalculating inside a render() call may cause corruption in certain
|
|
* cases.
|
|
* @param skipUpdate - Setting to `true` will stop the transforms of the scene graph from
|
|
* being updated. This means the calculation returned MAY be out of date BUT will give you a
|
|
* nice performance boost.
|
|
* @param rect - Optional rectangle to store the result of the bounds calculation.
|
|
* @returns - The minimum axis-aligned rectangle in world space that fits around this object.
|
|
*/
|
|
getBounds(skipUpdate, rect) {
|
|
return skipUpdate || (this.parent ? (this._recursivePostUpdateTransform(), this.updateTransform()) : (this.parent = this._tempDisplayObjectParent, this.updateTransform(), this.parent = null)), this._bounds.updateID !== this._boundsID && (this.calculateBounds(), this._bounds.updateID = this._boundsID), rect || (this._boundsRect || (this._boundsRect = new Rectangle()), rect = this._boundsRect), this._bounds.getRectangle(rect);
|
|
}
|
|
/**
|
|
* Retrieves the local bounds of the displayObject as a rectangle object.
|
|
* @param rect - Optional rectangle to store the result of the bounds calculation.
|
|
* @returns - The rectangular bounding area.
|
|
*/
|
|
getLocalBounds(rect) {
|
|
rect || (this._localBoundsRect || (this._localBoundsRect = new Rectangle()), rect = this._localBoundsRect), this._localBounds || (this._localBounds = new Bounds());
|
|
const transformRef = this.transform, parentRef = this.parent;
|
|
this.parent = null, this._tempDisplayObjectParent.worldAlpha = parentRef?.worldAlpha ?? 1, this.transform = this._tempDisplayObjectParent.transform;
|
|
const worldBounds = this._bounds, worldBoundsID = this._boundsID;
|
|
this._bounds = this._localBounds;
|
|
const bounds = this.getBounds(!1, rect);
|
|
return this.parent = parentRef, this.transform = transformRef, this._bounds = worldBounds, this._bounds.updateID += this._boundsID - worldBoundsID, bounds;
|
|
}
|
|
/**
|
|
* Calculates the global position of the display object.
|
|
* @param position - The world origin to calculate from.
|
|
* @param point - A Point object in which to store the value, optional
|
|
* (otherwise will create a new Point).
|
|
* @param skipUpdate - Should we skip the update transform.
|
|
* @returns - A point object representing the position of this object.
|
|
*/
|
|
toGlobal(position, point, skipUpdate = !1) {
|
|
return skipUpdate || (this._recursivePostUpdateTransform(), this.parent ? this.displayObjectUpdateTransform() : (this.parent = this._tempDisplayObjectParent, this.displayObjectUpdateTransform(), this.parent = null)), this.worldTransform.apply(position, point);
|
|
}
|
|
/**
|
|
* Calculates the local position of the display object relative to another point.
|
|
* @param position - The world origin to calculate from.
|
|
* @param from - The DisplayObject to calculate the global position from.
|
|
* @param point - A Point object in which to store the value, optional
|
|
* (otherwise will create a new Point).
|
|
* @param skipUpdate - Should we skip the update transform
|
|
* @returns - A point object representing the position of this object
|
|
*/
|
|
toLocal(position, from, point, skipUpdate) {
|
|
return from && (position = from.toGlobal(position, point, skipUpdate)), skipUpdate || (this._recursivePostUpdateTransform(), this.parent ? this.displayObjectUpdateTransform() : (this.parent = this._tempDisplayObjectParent, this.displayObjectUpdateTransform(), this.parent = null)), this.worldTransform.applyInverse(position, point);
|
|
}
|
|
/**
|
|
* Set the parent Container of this DisplayObject.
|
|
* @param container - The Container to add this DisplayObject to.
|
|
* @returns - The Container that this DisplayObject was added to.
|
|
*/
|
|
setParent(container) {
|
|
if (!container || !container.addChild)
|
|
throw new Error("setParent: Argument must be a Container");
|
|
return container.addChild(this), container;
|
|
}
|
|
/** Remove the DisplayObject from its parent Container. If the DisplayObject has no parent, do nothing. */
|
|
removeFromParent() {
|
|
this.parent?.removeChild(this);
|
|
}
|
|
/**
|
|
* Convenience function to set the position, scale, skew and pivot at once.
|
|
* @param x - The X position
|
|
* @param y - The Y position
|
|
* @param scaleX - The X scale value
|
|
* @param scaleY - The Y scale value
|
|
* @param rotation - The rotation
|
|
* @param skewX - The X skew value
|
|
* @param skewY - The Y skew value
|
|
* @param pivotX - The X pivot value
|
|
* @param pivotY - The Y pivot value
|
|
* @returns - The DisplayObject instance
|
|
*/
|
|
setTransform(x = 0, y = 0, scaleX = 1, scaleY = 1, rotation = 0, skewX = 0, skewY = 0, pivotX = 0, pivotY = 0) {
|
|
return this.position.x = x, this.position.y = y, this.scale.x = scaleX || 1, this.scale.y = scaleY || 1, this.rotation = rotation, this.skew.x = skewX, this.skew.y = skewY, this.pivot.x = pivotX, this.pivot.y = pivotY, this;
|
|
}
|
|
/**
|
|
* Base destroy method for generic display objects. This will automatically
|
|
* remove the display object from its parent Container as well as remove
|
|
* all current event listeners and internal references. Do not use a DisplayObject
|
|
* after calling `destroy()`.
|
|
* @param _options
|
|
*/
|
|
destroy(_options) {
|
|
this.removeFromParent(), this._destroyed = !0, this.transform = null, this.parent = null, this._bounds = null, this.mask = null, this.cullArea = null, this.filters = null, this.filterArea = null, this.hitArea = null, this.eventMode = "auto", this.interactiveChildren = !1, this.emit("destroyed"), this.removeAllListeners();
|
|
}
|
|
/**
|
|
* @protected
|
|
* @member {PIXI.Container}
|
|
*/
|
|
get _tempDisplayObjectParent() {
|
|
return this.tempDisplayObjectParent === null && (this.tempDisplayObjectParent = new TemporaryDisplayObject()), this.tempDisplayObjectParent;
|
|
}
|
|
/**
|
|
* Used in Renderer, cacheAsBitmap and other places where you call an `updateTransform` on root.
|
|
*
|
|
* ```js
|
|
* const cacheParent = elem.enableTempParent();
|
|
* elem.updateTransform();
|
|
* elem.disableTempParent(cacheParent);
|
|
* ```
|
|
* @returns - Current parent
|
|
*/
|
|
enableTempParent() {
|
|
const myParent = this.parent;
|
|
return this.parent = this._tempDisplayObjectParent, myParent;
|
|
}
|
|
/**
|
|
* Pair method for `enableTempParent`
|
|
* @param cacheParent - Actual parent of element
|
|
*/
|
|
disableTempParent(cacheParent) {
|
|
this.parent = cacheParent;
|
|
}
|
|
/**
|
|
* The position of the displayObject on the x axis relative to the local coordinates of the parent.
|
|
* An alias to position.x
|
|
*/
|
|
get x() {
|
|
return this.position.x;
|
|
}
|
|
set x(value) {
|
|
this.transform.position.x = value;
|
|
}
|
|
/**
|
|
* The position of the displayObject on the y axis relative to the local coordinates of the parent.
|
|
* An alias to position.y
|
|
*/
|
|
get y() {
|
|
return this.position.y;
|
|
}
|
|
set y(value) {
|
|
this.transform.position.y = value;
|
|
}
|
|
/**
|
|
* Current transform of the object based on world (parent) factors.
|
|
* @readonly
|
|
*/
|
|
get worldTransform() {
|
|
return this.transform.worldTransform;
|
|
}
|
|
/**
|
|
* Current transform of the object based on local factors: position, scale, other stuff.
|
|
* @readonly
|
|
*/
|
|
get localTransform() {
|
|
return this.transform.localTransform;
|
|
}
|
|
/**
|
|
* The coordinate of the object relative to the local coordinates of the parent.
|
|
* @since 4.0.0
|
|
*/
|
|
get position() {
|
|
return this.transform.position;
|
|
}
|
|
set position(value) {
|
|
this.transform.position.copyFrom(value);
|
|
}
|
|
/**
|
|
* The scale factors of this object along the local coordinate axes.
|
|
*
|
|
* The default scale is (1, 1).
|
|
* @since 4.0.0
|
|
*/
|
|
get scale() {
|
|
return this.transform.scale;
|
|
}
|
|
set scale(value) {
|
|
this.transform.scale.copyFrom(value);
|
|
}
|
|
/**
|
|
* 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).
|
|
* @since 4.0.0
|
|
*/
|
|
get pivot() {
|
|
return this.transform.pivot;
|
|
}
|
|
set pivot(value) {
|
|
this.transform.pivot.copyFrom(value);
|
|
}
|
|
/**
|
|
* The skew factor for the object in radians.
|
|
* @since 4.0.0
|
|
*/
|
|
get skew() {
|
|
return this.transform.skew;
|
|
}
|
|
set skew(value) {
|
|
this.transform.skew.copyFrom(value);
|
|
}
|
|
/**
|
|
* The rotation of the object in radians.
|
|
* 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees.
|
|
*/
|
|
get rotation() {
|
|
return this.transform.rotation;
|
|
}
|
|
set rotation(value) {
|
|
this.transform.rotation = value;
|
|
}
|
|
/**
|
|
* The angle of the object in degrees.
|
|
* 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees.
|
|
*/
|
|
get angle() {
|
|
return this.transform.rotation * RAD_TO_DEG;
|
|
}
|
|
set angle(value) {
|
|
this.transform.rotation = value * DEG_TO_RAD;
|
|
}
|
|
/**
|
|
* 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 display objects within the same container.
|
|
* @see PIXI.Container#sortableChildren
|
|
*/
|
|
get zIndex() {
|
|
return this._zIndex;
|
|
}
|
|
set zIndex(value) {
|
|
this._zIndex !== value && (this._zIndex = value, this.parent && (this.parent.sortDirty = !0));
|
|
}
|
|
/**
|
|
* Indicates if the object is globally visible.
|
|
* @readonly
|
|
*/
|
|
get worldVisible() {
|
|
let item = this;
|
|
do {
|
|
if (!item.visible)
|
|
return !1;
|
|
item = item.parent;
|
|
} while (item);
|
|
return !0;
|
|
}
|
|
/**
|
|
* Sets a mask for the displayObject. A mask is an object that limits the visibility of an
|
|
* object to the shape of the mask applied to it. In PixiJS a regular mask must be a
|
|
* {@link PIXI.Graphics} or a {@link PIXI.Sprite} object. This allows for much faster masking in canvas as it
|
|
* utilities shape clipping. Furthermore, a mask of an object must be in the subtree of its parent.
|
|
* Otherwise, `getLocalBounds` may calculate incorrect bounds, which makes the container's width and height wrong.
|
|
* To remove a mask, set this property to `null`.
|
|
*
|
|
* For sprite mask both alpha and red channel are used. Black mask is the same as transparent mask.
|
|
* @example
|
|
* import { Graphics, Sprite } from 'pixi.js';
|
|
*
|
|
* const graphics = new Graphics();
|
|
* graphics.beginFill(0xFF3300);
|
|
* graphics.drawRect(50, 250, 100, 100);
|
|
* graphics.endFill();
|
|
*
|
|
* const sprite = new Sprite(texture);
|
|
* sprite.mask = graphics;
|
|
* @todo At the moment, CanvasRenderer doesn't support Sprite as mask.
|
|
*/
|
|
get mask() {
|
|
return this._mask;
|
|
}
|
|
set mask(value) {
|
|
if (this._mask !== value) {
|
|
if (this._mask) {
|
|
const maskObject = this._mask.isMaskData ? this._mask.maskObject : this._mask;
|
|
maskObject && (maskObject._maskRefCount--, maskObject._maskRefCount === 0 && (maskObject.renderable = !0, maskObject.isMask = !1));
|
|
}
|
|
if (this._mask = value, this._mask) {
|
|
const maskObject = this._mask.isMaskData ? this._mask.maskObject : this._mask;
|
|
maskObject && (maskObject._maskRefCount === 0 && (maskObject.renderable = !1, maskObject.isMask = !0), maskObject._maskRefCount++);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
class TemporaryDisplayObject extends DisplayObject {
|
|
constructor() {
|
|
super(...arguments), this.sortDirty = null;
|
|
}
|
|
}
|
|
DisplayObject.prototype.displayObjectUpdateTransform = DisplayObject.prototype.updateTransform;
|
|
export {
|
|
DisplayObject,
|
|
TemporaryDisplayObject
|
|
};
|
|
//# sourceMappingURL=DisplayObject.mjs.map
|