import Timeline from './Timeline';
import utils from './utils';
//import { Container } from '../core/display/Container';
import * as core from '../core';
import * as soundPlayer from './sound';
//const Container = PIXI.Container;
const SharedTicker = core.ticker.shared;
/**
* Provide timeline playback of movieclip
* @memberof PIXI.animate
* @class MovieClip
* @extends PIXI.Container
* @constructor
* @param {Object|int} [options] The options object or the mode to play
* @param {int} [options.mode=0] The playback mode default is independent (0),
* @param {int} [options.startPosition=0] The starting frame
* @param {Boolean} [options.loop=true] If playback is looped
* @param {Object} [options.labels] The frame labels map of label to frames
* @param {int} [options.duration] The duration, if no duration is provided, auto determines length
* @param {int} [options.framerate=24] The framerate to use for independent mode
*/
class MovieClip extends core.Container {
constructor(options, duration, loop, framerate, labels) {
super();
// Default options
options = options === undefined ? {} : options;
// Options can also be the mode
if (typeof options === 'number') {
options = {
mode: options || MovieClip.INDEPENDENT,
duration: duration || 0,
loop: loop === undefined ? true : loop,
labels: labels || {},
framerate: framerate || 0,
startPosition: 0
};
} else {
// Apply defaults to options
options = Object.assign({
mode: MovieClip.INDEPENDENT,
startPosition: 0,
loop: true,
labels: {},
duration: 0,
framerate: 0
}, options);
}
/**
* Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED).
* See each constant for a description of the behaviour.
* @name PIXI.animate.MovieClip#mode
* @type int
* @default null
*/
this.mode = options.mode;
/**
* Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME.
* @name PIXI.animate.MovieClip#startPosition
* @type Number
* @default 0
*/
this.startPosition = options.startPosition;
/**
* Indicates whether this MovieClip should loop when it reaches the end of its timeline.
* @name PIXI.animate.MovieClip#loop
* @type Boolean
* @default true
*/
this.loop = !!options.loop;
/**
* The current frame of the movieclip.
* @name PIXI.animate.MovieClip#currentFrame
* @type Number
* @default 0
* @readOnly
*/
this.currentFrame = 0;
/**
* The collection of private labels
* @name PIXI.animate.MovieClip#_labels
* @type Array
* @private
*/
this._labels = [];
/**
* The collection of private labels
* @name PIXI.animate.MovieClip#_labelDict
* @type Object
* @private
*/
this._labelDict = options.labels;
if (options.labels) {
for (let name in options.labels) {
let label = {
label: name,
position: options.labels[name]
};
this._labels.push(label);
}
this._labels.sort(function(a, b) {
return a.position - b.position;
});
}
/**
* If true, this movieclip will animate automatically whenever it is on the stage.
* @name PIXI.animate.MovieClip#selfAdvance
* @type Boolean
* @default true
*/
this.selfAdvance = true;
/**
* If true, the MovieClip's position will not advance when ticked.
* @name PIXI.animate.MovieClip#paused
* @type Boolean
* @default false
*/
this.paused = false;
/**
* If true, actions in this MovieClip's tweens will be run when the playhead advances.
* @name PIXI.animate.MovieClip#actionsEnabled
* @type Boolean
* @default true
*/
this.actionsEnabled = true;
/**
* If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds
* it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT.
* <br><br>
* For example, if you had a character animation with a 'body' child MovieClip instance
* with different costumes on each frame, you could set body.autoReset = false, so that
* you can manually change the frame it is on, without worrying that it will be reset
* automatically.
* @name PIXI.animate.MovieClip#autoReset
* @type Boolean
* @default true
*/
this.autoReset = true;
/**
* @name PIXI.animate.MovieClip#_synchOffset
* @type Number
* @default 0
* @private
*/
this._synchOffset = 0;
/**
* @name PIXI.animate.MovieClip#_prevPos
* @type Number
* @default -1
* @private
*/
this._prevPos = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1.
/**
* Note - changed from default: When the MovieClip is framerate independent, this is the time
* elapsed from frame 0 in seconds.
* @name PIXI.animate.MovieClip#_t
* @type Number
* @default 0
* @private
*/
this._t = 0;
/**
* By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip
* will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
* framerate.
*
* @name PIXI.animate.MovieClip#_framerate
* @type {Number}
* @default 0
* @protected
*/
this._framerate = options.framerate;
/**
* The total time in seconds for the animation. This is changed when setting the framerate.
* @name PIXI.animate.MovieClip#_duration
* @type Number
* @default 0
* @private
*/
this._duration = 0;
/**
* The total duration in frames for the animation.
* @name PIXI.animate.MovieClip#_totalFrames
* @type Number
* @default 0
* @private
*/
this._totalFrames = options.duration;
/**
* Standard tween timelines for all objects. Each element in the _timelines array
* is a Timeline object - an array of tweens for one target, in order of occurrence.
* @name PIXI.animate.MovieClip#_timelines
* @type Array
* @protected
*/
this._timelines = [];
/**
* Array of child timelines denoting if a child is actively a child of this movieclip
* on any given frame. Each element in the _timedChildTimelines is an array with a 'target'
* property, and is an array of boolean values indexed by frame.
* @name PIXI.animate.MovieClip#_timedChildTimelines
* @type {Array}
* @protected
*/
this._timedChildTimelines = [];
/**
* Array to depth sort timed children
* @name PIXI.animate.MovieClip#_depthSorted
* @type {Array}
* @private
*/
this._depthSorted = [];
/**
* Array of frame scripts, indexed by frame.
* @name PIXI.animate.MovieClip#_actions
* @type {Array}
* @protected
*/
this._actions = [];
/**
* Optional callback fired before timeline is updated.
* Can be used to clamp or update the currentFrame.
* @name PIXI.animate.MovieClip#_beforeUpdate
* @type {Function}
* @private
*/
this._beforeUpdate = null;
if (this.mode === MovieClip.INDEPENDENT) {
this._tickListener = this._tickListener.bind(this);
this._onAdded = this._onAdded.bind(this);
this._onRemoved = this._onRemoved.bind(this);
this.on('added', this._onAdded);
this.on('removed', this._onRemoved);
}
if (options.framerate) {
this.framerate = options.framerate;
}
//save often used methods on the instance so that they can be fetched slightly faster
//than if they had to be fetched from the prototype
this.advance = this.advance;
this._updateTimeline = this._updateTimeline;
this._setTimelinePosition = this._setTimelinePosition;
this._goto = this._goto;
}
_onAdded() {
if (!this._framerate) {
this.framerate = this.parentFramerate;
}
SharedTicker.add(this._tickListener);
}
_tickListener(tickerDeltaTime) {
if (this.paused || !this.selfAdvance) {
//see if the movieclip needs to be updated even though it isn't animating
if (this._prevPos < 0) {
this._goto(this.currentFrame);
}
return;
}
let seconds = tickerDeltaTime / core.settings.TARGET_FPMS / 1000;
this.advance(seconds);
}
_onRemoved() {
SharedTicker.remove(this._tickListener);
}
/**
* Returns an array of objects with label and position (aka frame) properties, sorted by position.
* @name PIXI.animate.MovieClip#labels
* @type {Array}
* @readonly
*/
get labels() {
return this._labels;
}
/**
* Returns a dictionary of labels where key is the label and value is the frame.
* @name PIXI.animate.MovieClip#labelsMap
* @type {Object}
* @readonly
*/
get labelsMap() {
return this._labelDict;
}
/**
* Returns the name of the label on or immediately before the current frame.
* @name PIXI.animate.MovieClip#currentLabel
* @type {String}
* @readonly
*/
get currentLabel() {
let labels = this._labels;
let current = null;
for (let i = 0, len = labels.length; i < len; ++i) {
if (labels[i].position <= this.currentFrame) {
current = labels[i].label;
} else {
break;
}
}
return current;
}
/**
* When the MovieClip is framerate independent, this is the time elapsed from frame 0 in seconds.
* @name PIXI.animate.MovieClip#elapsedTime
* @type Number
* @default 0
* @public
*/
get elapsedTime() {
return this._t;
}
set elapsedTime(value) {
this._t = value;
}
/**
* By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip
* will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
* framerate.
*
* For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will
* advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
* vary slightly between frames.
*
* This feature is dependent on the tick event object (or an object with an appropriate 'delta' property) being
* passed into {{#crossLink 'Stage/update'}}{{/crossLink}}.
* @name PIXI.animate.MovieClip#framerate
* @type {Number}
* @default 0
*/
get framerate() {
return this._framerate;
}
set framerate(value) {
if (value > 0) {
if (this._framerate) {
//recalculate time based on difference between new and old framerate:
this._t *= this._framerate / value;
} else {
this._t = this.currentFrame / value;
}
this._framerate = value;
this._duration = value ? this._totalFrames / value : 0;
} else {
this._t = this._framerate = this._duration = 0;
}
}
/**
* Get the total number of frames (duration) of this MovieClip
* @name PIXI.animate.MovieClip#totalFrames
* @type {Number}
* @default 0
* @readOnly
*/
get totalFrames() {
return this._totalFrames;
}
/**
* Extend the timeline to the last frame.
* @method PIXI.animate.MovieClip#_autoExtend
* @private
* @param {int} endFrame
*/
_autoExtend(endFrame) {
if (this._totalFrames < endFrame) {
this._totalFrames = endFrame;
}
}
/**
* Convert values of properties
* @method PIXI.animate.MovieClip#_parseProperties
* @private
* @param {Object} properties
*/
_parseProperties(properties) {
// Convert any string colors to uints
if (typeof properties.t === 'string') {
properties.t = utils.hexToUint(properties.t);
} else if (typeof properties.v === 'number') {
properties.v = !!properties.v;
}
}
/**
* Get a timeline for a child, synced timeline.
* @method PIXI.animate.MovieClip#_getChildTimeline
* @private
* @param {PIXI.animate.MovieClip} instance
* @return {PIXI.animate.Timeline}
*/
_getChildTimeline(instance) {
for (let i = this._timelines.length - 1; i >= 0; --i) {
if (this._timelines[i].target === instance) {
return this._timelines[i];
}
}
let timeline = new Timeline(instance);
this._timelines.push(timeline);
return timeline;
}
/**
* Add mask or masks
* @method PIXI.animate.MovieClip#addTimedMask
* @param {PIXI.DisplayObject} instance Instance to mask
* @param {Object} keyframes The map of frames to mask objects
* @return {PIXI.animate.MovieClip} instance of clip for chaining
*/
addTimedMask(instance, keyframes) {
for (let i in keyframes) {
this.addKeyframe(instance, {
m: keyframes[i]
}, parseInt(i, 10));
}
// Set the initial position/add
this._setTimelinePosition(this.currentFrame, this.currentFrame, true);
return this;
}
/**
* Shortcut alias for `addTimedMask`
* @method PIXI.animate.MovieClip#am
* @param {PIXI.DisplayObject} instance Instance to mask
* @param {Object} keyframes The map of frames to mask objects
* @return {PIXI.animate.MovieClip} instance of clip for chaining
*/
am(instance, keyframes) {
return this.addTimedMask(instance, keyframes);
}
/**
* Add a tween to the clip
* @method PIXI.animate.MovieClip#addTween
* @param {PIXI.DisplayObject} instance The clip to tween
* @param {Object} properties The property or property to tween
* @param {int} startFrame The frame to start tweening
* @param {int} [duration=0] Number of frames to tween. If 0, then the properties are set
* with no tweening.
* @param {Function} [ease] An optional easing function that takes the tween time from 0-1.
* @return {PIXI.animate.MovieClip}
*/
addTween(instance, properties, startFrame, duration, ease) {
let timeline = this._getChildTimeline(instance);
this._parseProperties(properties);
timeline.addTween(properties, startFrame, duration, ease);
this._autoExtend(startFrame + duration);
return this;
}
/**
* Add a tween to the clip
* @method PIXI.animate.MovieClip#addKeyframe
* @param {PIXI.DisplayObject} instance The clip to tween
* @param {Object} properties The property or property to tween
* @param {int} startFrame The frame to start tweening
* @param {int} [duration=0] Number of frames to tween. If 0, then the properties are set
* with no tweening.
* @param {Function} [ease] An optional easing function that takes the tween time from 0-1.
* @return {PIXI.animate.MovieClip}
*/
addKeyframe(instance, properties, startFrame) {
let timeline = this._getChildTimeline(instance);
this._parseProperties(properties);
timeline.addKeyframe(properties, startFrame);
this._autoExtend(startFrame);
return this;
}
/**
* Alias for method `addTimedChild`
* @method PIXI.animate.MovieClip#at
* @return {PIXI.animate.MovieClip}
*/
at(instance, startFrame, duration, keyframes) {
return this.addTimedChild(instance, startFrame, duration, keyframes);
}
/**
* Add a child to show for a certain number of frames before automatic removal.
* @method PIXI.animate.MovieClip#addTimedChild
* @param {PIXI.DisplayObject} instance The clip to show
* @param {int} startFrame The starting frame
* @param {int} [duration=1] The number of frames to display the child before removing it.
* @param {String|Array} [keyframes] The collection of static keyframes to add
* @return {PIXI.animate.MovieClip}
*/
addTimedChild(instance, startFrame, duration, keyframes) {
if (startFrame === undefined) // jshint ignore:line
{
startFrame = 0;
}
if (duration === undefined || duration < 1) // jshint ignore:line
{
duration = this._totalFrames || 1;
}
// Add the starting offset for synced movie clips
if (instance.mode === MovieClip.SYNCHED) {
instance.parentStartPosition = startFrame;
}
//add tweening info about this child's presence on stage
//when the child is (re)added, if it has 'autoReset' set to true, then it
//should be set back to frame 0
let timeline, i;
//get existing timeline
for (i = this._timedChildTimelines.length - 1; i >= 0; --i) {
if (this._timedChildTimelines[i].target === instance) {
timeline = this._timedChildTimelines[i];
break;
}
}
//if there wasn't one, make a new one
if (!timeline) {
timeline = [];
timeline.target = instance;
this._timedChildTimelines.push(timeline);
}
// Fill the timeline with keyframe booleans
utils.fillFrames(timeline, startFrame, duration);
// Update the total frames if the instance extends our current
// total frames for this movieclip
if (this._totalFrames < startFrame + duration) {
this._totalFrames = startFrame + duration;
}
// Add the collection of keyframes
if (keyframes) {
if (typeof keyframes === "string") {
keyframes = utils.deserializeKeyframes(keyframes);
}
// Convert the keyframes object into
// individual properties
let lastFrame = {};
for (let i in keyframes) {
lastFrame = Object.assign({}, lastFrame, keyframes[i]);
this.addKeyframe(instance, lastFrame, parseInt(i, 10));
}
this._getChildTimeline(instance)
.extendLastFrame(startFrame + duration);
}
// Set the initial position/add
this._setTimelinePosition(startFrame, this.currentFrame, true);
return this;
}
/**
* Short cut for `addAction`
* @method PIXI.animate.MovieClip#aa
* @param {Function} callback The clip call on a certain frame
* @param {int|String} startFrame The starting frame index or label
* @return {PIXI.animate.MovieClip}
*/
aa(callback, startFrame) {
return this.addAction(callback, startFrame);
}
/**
* Handle frame actions, callback is bound to the instance of the MovieClip.
* @method PIXI.animate.MovieClip#addAction
* @param {Function} callback The clip call on a certain frame
* @param {int|String} startFrame The starting frame index or label
* @return {PIXI.animate.MovieClip}
*/
addAction(callback, startFrame) {
if (typeof startFrame === 'string') {
const index = this._labelDict[startFrame];
if (index === undefined) {
throw `The label '${startFrame}' does not exist on this timeline`;
}
startFrame = index;
}
let actions = this._actions;
//ensure that the movieclip timeline is long enough to support the target frame
if (actions.length <= startFrame) {
actions.length = startFrame + 1;
}
if (this._totalFrames < startFrame) {
this._totalFrames = startFrame;
}
//add the action
if (actions[startFrame]) {
actions[startFrame].push(callback);
} else {
actions[startFrame] = [callback];
}
return this;
}
/**
* Short cut for `playSound`
* @method PIXI.animate.MovieClip#ps
* @param {String} alias The name of the Sound
* @param {Boolean} [loop=false] The loop property of the sound
* @param {MovieClip} context The MovieClip the sound originates from
* @return {PIXI.animate.MovieClip}
*/
ps(alias, loop) {
return this.playSound(alias, loop);
}
/**
* Handle sounds.
* @method PIXI.animate.MovieClip#playSound
* @param {String} alias The name of the Sound
* @param {Boolean} [loop=false] The loop property of the sound
* @param {MovieClip} context The MovieClip the sound originates from
* @return {PIXI.animate.MovieClip}
*/
playSound(alias, loop) {
soundPlayer.emit('play', alias, !!loop, this);
return this;
}
/**
* Sets paused to false.
* @method PIXI.animate.MovieClip#play
*/
play() {
this.paused = false;
}
/**
* Sets paused to true.
* @method PIXI.animate.MovieClip#stop
*/
stop() {
this.paused = true;
}
/**
* Advances this movie clip to the specified position or label and sets paused to false.
* @method PIXI.animate.MovieClip#gotoAndPlay
* @param {String|Number} positionOrLabel The animation name or frame number to go to.
*/
gotoAndPlay(positionOrLabel) {
this.paused = false;
this._goto(positionOrLabel);
}
/**
* Advances this movie clip to the specified position or label and sets paused to true.
* @method PIXI.animate.MovieClip#gotoAndStop
* @param {String|Number} positionOrLabel The animation or frame name to go to.
*/
gotoAndStop(positionOrLabel) {
this.paused = true;
this._goto(positionOrLabel);
}
/**
* Get the close parent with a valid framerate. If no parent, returns the default framerate.
* @name PIXI.animate.MovieClip#parentFramerate
* @type {Number}
* @readOnly
*/
get parentFramerate() {
let o = this,
fps = o._framerate;
while ((o = o.parent) && !fps) {
if (o.mode === MovieClip.INDEPENDENT) {
fps = o._framerate;
}
}
return fps || MovieClip.DEFAULT_FRAMERATE;
}
/**
* Advances the playhead. This occurs automatically each tick by default.
* @method PIXI.animate.MovieClip#advance
* @param [time] {Number} The amount of time in seconds to advance by. Only applicable if framerate is set.
*/
advance(time) {
// Handle any other cases where starting to play
// and no framerate has been set yet
if (!this._framerate) {
this.framerate = this.parentFramerate;
}
if (time) {
this._t += time;
}
if (this._t > this._duration) {
this._t = this.loop ? this._t % this._duration : this._duration;
}
//add a tiny amount to account for potential floating point errors
this.currentFrame = Math.floor(this._t * this._framerate + 0.00000001);
//final error checking
if (this.currentFrame >= this._totalFrames) {
this.currentFrame = this._totalFrames - 1;
}
let afterUpdateOnce;
if (this._beforeUpdate) {
afterUpdateOnce = this._beforeUpdate(this);
}
//update all tweens & actions in the timeline
this._updateTimeline();
// Do the animator callback here
if (afterUpdateOnce) {
afterUpdateOnce();
}
}
/**
* @method PIXI.animate.MovieClip#_goto
* @param {String|Number} positionOrLabel The animation name or frame number to go to.
* @protected
*/
_goto(positionOrLabel) {
let pos = typeof positionOrLabel === 'string' ? this._labelDict[positionOrLabel] : positionOrLabel;
if (pos === undefined) // jshint ignore:line
{
return;
}
// prevent _updateTimeline from overwriting the new position because of a reset:
this._prevPos = NaN;
this.currentFrame = pos;
// Handle the case where trying to play but haven't
// added to the stage yet
if (!this._framerate) {
this.framerate = this.parentFramerate;
}
//update the elapsed time if a time based movieclip
if (this._framerate > 0) {
this._t = pos / this._framerate;
} else {
this._t = 0;
}
this._updateTimeline();
}
/**
* @method PIXI.animate.MovieClip#_reset
* @private
*/
_reset() {
this._prevPos = -1;
this._t = 0;
this.currentFrame = 0;
}
/**
* @method PIXI.animate.MovieClip#_updateTimeline
* @protected
*/
_updateTimeline() {
let synched = this.mode !== MovieClip.INDEPENDENT;
if (synched) {
this.currentFrame = this.startPosition + (this.mode === MovieClip.SINGLE_FRAME ? 0 : this._synchOffset);
if (this.currentFrame >= this._totalFrames) {
this.currentFrame %= this._totalFrames;
}
}
if (this._prevPos === this.currentFrame) {
return;
}
// update timeline position, ignoring actions if this is a graphic.
this._setTimelinePosition(this._prevPos, this.currentFrame, synched ? false : this.actionsEnabled);
this._prevPos = this.currentFrame;
}
/**
* Set the timeline position
* @method PIXI.animate.MovieClip#_setTimelinePosition
* @protected
* @param {int} startFrame
* @param {int} currentFrame
* @param {Boolean} doActions
*/
_setTimelinePosition(startFrame, currentFrame, doActions) {
if (startFrame !== currentFrame && doActions) {
let startPos = isNaN(startFrame) ? currentFrame : (startFrame >= this._totalFrames - 1 ? 0 : startFrame + 1);
// generate actionFrames on the way
let actionFrames = []; // number[]
// loop
if (currentFrame < startPos) {
for (let i = startPos; i < this._actions.length; ++i) {
this._actions[i] && actionFrames.push(i);
}
for (let i = 0; i <= currentFrame; ++i) {
this._actions[i] && actionFrames.push(i);
}
}
// no loop
else {
for (let i = startPos; i <= currentFrame; ++i) {
this._actions[i] && actionFrames.push(i);
}
}
if (actionFrames.length) {
let oldCurrentFrame = this.currentFrame;
for (let i = 0; i < actionFrames.length; ++i) {
let frame = actionFrames[i];
this._setTimelinePosition(frame, frame, true);
// _goto is called OR last frame reached
if (this.currentFrame !== oldCurrentFrame || frame === currentFrame) {
return;
}
// stop is called
else if (this.paused) {
this.currentFrame = frame;
return;
}
}
}
}
//handle all tweens
let i, j, length, _timelines = this._timelines;
for (i = _timelines.length - 1; i >= 0; --i) {
let timeline = _timelines[i];
for (j = 0, length = timeline.length; j < length; ++j) {
let tween = timeline[j];
//if the tween contains part of the timeline that we are travelling through
if (currentFrame >= tween.startFrame &&
currentFrame <= tween.endFrame) {
// set the position within that tween
//and break the loop to move onto the next timeline
tween.setPosition(currentFrame);
break;
}
}
}
let timedChildTimelines = this._timedChildTimelines;
let depthSorted = this._depthSorted;
for (i = 0, length = timedChildTimelines.length; i < length; ++i) {
let target = timedChildTimelines[i].target;
let shouldBeChild = timedChildTimelines[i][currentFrame];
//if child should be on stage and is not:
if (shouldBeChild) {
// Add to the depthSorted object so we can
// check that items are property drawn later
depthSorted.push(target);
if (target.parent !== this) {
// add the target if it's not there already
this.addChild(target);
if (target.mode === MovieClip.INDEPENDENT && target.autoReset) {
target._reset();
}
}
} else if (!shouldBeChild && target.parent === this) {
this.removeChild(target);
}
}
// Properly depth sort the children
for (i = 0, length = depthSorted.length; i < length; i++) {
let target = depthSorted[i];
let currentIndex = this.children.indexOf(target);
if (currentIndex !== i) {
this.addChildAt(target, i);
}
}
// Clear the temporary depth sorting array
depthSorted.length = 0;
//go through all children and update synched movieclips that are not single frames
let children = this.children,
child;
for (i = 0, length = children.length; i < length; ++i) {
child = children[i];
if (child.mode === MovieClip.SYNCHED) {
child._synchOffset = currentFrame - child.parentStartPosition;
child._updateTimeline();
}
}
//handle actions
if (doActions && this._actions && this._actions[currentFrame]) {
let frameActions = this._actions[currentFrame];
for (let j = 0; j < frameActions.length; ++j) {
frameActions[j].call(this);
}
}
}
destroy(destroyChildren) {
if (this._tickListener) {
SharedTicker.remove(this._tickListener);
this._tickListener = null;
}
const hiddenChildren = [];
let timelines = this._timelines;
if (timelines) {
for (let i = 0; i < timelines.length; i++) {
const timeline = timelines[i];
hiddenChildren.push(timeline.target);
timeline._currentProps = null;
timeline.length = 0;
}
}
timelines = this._timedChildTimelines;
if (timelines) {
for (let i = 0; i < timelines.length; i++) {
const timeline = timelines[i];
if (hiddenChildren.indexOf(timeline.target) < 0) {
hiddenChildren.push(timeline.target);
}
timeline._currentProps = null;
timeline.length = 0;
}
}
// Destroy all the children
for (let i = 0; i < hiddenChildren.length; i++) {
// Don't destroy children in the display list
if (this.children.indexOf(hiddenChildren[i]) < 0) {
hiddenChildren[i].destroy(destroyChildren);
}
}
hiddenChildren.length = 0;
this._actions = null;
this._timelines = null;
this._depthSorted = null;
this._timedChildTimelines = null;
this._beforeUpdate = null;
this._labels = null;
this._labelDict = null;
super.destroy(destroyChildren);
}
}
/**
* The MovieClip will advance independently of its parent, even if its parent is paused.
* This is the default mode.
* @name PIXI.animate.MovieClip.INDEPENDENT
* @static
* @type String
* @default 0
* @readonly
*/
MovieClip.INDEPENDENT = 0;
/**
* The MovieClip will only display a single frame (as determined by the startPosition property).
* @name PIXI.animate.MovieClip.SINGLE_FRAME
* @static
* @type String
* @default 1
* @readonly
*/
MovieClip.SINGLE_FRAME = 1;
/**
* The MovieClip will be advanced only when its parent advances and will be synched to the position of
* the parent MovieClip.
* @name PIXI.animate.MovieClip.SYNCHED
* @static
* @type String
* @default 2
* @readonly
*/
MovieClip.SYNCHED = 2;
/**
* The default framerate if none is specified or there's not parent clip with a framerate.
* @name PIXI.animate.MovieClip.DEFAULT_FRAMERATE
* @static
* @type Number
* @default 24
* @readonly
*/
MovieClip.DEFAULT_FRAMERATE = 24;
/**
* Extend a container
* @method PIXI.animate.MovieClip.extend
* @static
* @param {PIXI.animate.MovieClip} child The child function
* @return {PIXI.animate.MovieClip} The child
*/
/**
* Extend a container (alias for `extend`)
* @method PIXI.animate.MovieClip.e
* @static
* @param {PIXI.animate.MovieClip} child The child function
* @return {PIXI.animate.MovieClip} The child
*/
MovieClip.extend = MovieClip.e = function(child) {
child.prototype = Object.create(MovieClip.prototype);
child.prototype.__parent = MovieClip.prototype;
child.prototype.constructor = child;
return child;
};
// Assign to namespace
export default MovieClip;