Source: animate/Animator.js

animate/Animator.js

import AnimatorTimeline from './AnimatorTimeline';

// Static collection of timelines
const timelines = [];

/**
 * Play animation via start/stop frame labels
 * @class Animator
 * @memberof PIXI.animate
 */
class Animator {

    /**
     * The collection of timelines
     * @name {Array<PIXI.animate.AnimatorTimeline>} PIXI.animate.Animator#_timelines
     * @private
     * @static
     */
    static get _timelines() {
        return timelines;
    }

    /**
     * Suffix added to label for a stop.
     * @name {String} PIXI.animate.Animator.STOP_LABEL
     * @static
     * @default "_stop"
     */
    static get STOP_LABEL() {
        return "_stop";
    }

    /**
     * Suffix added to label for a loop.
     * @name {String} PIXI.animate.Animator.LOOP_LABEL
     * @static
     * @default "_loop"
     */
    static get LOOP_LABEL() {
        return "_loop";
    }

    /**
     * Play an animation by frame labels. For instance, play animation sequence from
     * "idle" to "idle_stop" or "idle_loop". If no event label is provided, will
     * play the entire duration of the MovieClip.
     * @method PIXI.animate.Animator#play
     * @static
     * @param {PIXI.animate.MovieClip} instance Movie clip to play.
     * @param {String|Function} [label] The frame label event to call, if no event is provided
     *        will use the entire length of the MovieClip. Can also be the callback.
     * @param {Function} [callback] Optional callback when complete
     * @return {PIXI.animate.AnimatorTimeline} Timeline object for stopping or getting progress.
     */
    static play(instance, label, callback) {
        let loop = false;
        let start, end;
        const labelIsFunction = typeof label === "function";
        if (label === undefined || labelIsFunction) {
            start = 0;
            end = instance.totalFrames - 1;
            if (labelIsFunction) {
                callback = label;
            }
        } else {
            start = instance.labelsMap[label];
            end = instance.labelsMap[label + this.STOP_LABEL];
            if (end === undefined) {
                end = instance.labelsMap[label + this.LOOP_LABEL];
                loop = true;
            }
            if (start === undefined) {
                throw new Error("No start label matching '" + label + "'");
            } else if (end === undefined) {
                throw new Error("No end label matching '" + label + "'");
            }
        }
        return this.fromTo(
            instance,
            start,
            end,
            loop,
            callback
        );
    }

    /**
     * Play an animation from the current frame to an end frame or label.
     * @method PIXI.animate.Animator#to
     * @static
     * @param {PIXI.animate.MovieClip} instance Movie clip to play.
     * @param {String|Number} end The end frame or label.
     * @param {Function} [callback] Optional callback when complete
     * @return {PIXI.animate.AnimatorTimeline} Timeline object for stopping or getting progress.
     */
    static to(instance, end, callback) {
        return this.fromTo(
            instance,
            instance.currentFrame,
            end,
            false,
            callback
        );
    }

    /**
     * Play a MovieClip from a start to end frame.
     * @method PIXI.animate.Animator#fromTo
     * @static
     * @param {PIXI.animate.MovieClip} instance Movie clip to play.
     * @param {Number|String} start The starting frame index or label.
     * @param {Number|String} end The ending frame index or label.
     * @param {Boolean} [loop=false] If the animation should loop.
     * @param {Function} [callback] Optional callback when complete
     * @return {PIXI.animate.AnimatorTimeline} Timeline object for stopping or getting progress.
     */
    static fromTo(instance, start, end, loop, callback) {

        if (typeof start === "string") {
            const startLabel = start;
            start = instance.labelsMap[startLabel];
            if (start === undefined) {
                throw new Error("No start label matching '" + startLabel + "'");
            }
        }
        if (typeof end === "string") {
            const endLabel = end;
            end = instance.labelsMap[endLabel];
            if (end === undefined) {
                throw new Error("No end label matching '" + endLabel + "'");
            }
        }
        if (start < 0) {
            throw new Error('Start frame is out of bounds');
        }
        if (end >= instance.totalFrames) {
            throw new Error('End frame is out of bounds');
        }
        if (start >= end) {
            throw new Error('End frame is before start frame');
        }

        // Stop any animation that's playing
        this.stop(instance);

        loop = !!loop;

        // Add a new timeline
        const timeline = AnimatorTimeline.create(
            instance,
            start,
            end,
            loop,
            callback
        );
        this._timelines.push(timeline);

        // Set the current frame
        if (instance.currentFrame !== start) {
            instance.gotoAndPlay(start);
        } else {
            instance.play();
        }
        return timeline;
    }

    /**
     * Stop the animation by instance.
     * @method PIXI.animate.Animator#stop
     * @static
     * @param {PIXI.animate.MovieClip} instance Movie clip to play.
     */
    static stop(instance) {
        for (let i = 0, len = this._timelines.length; i < len; i++) {
            const timeline = this._timelines[i];
            if (timeline.instance === instance) {
                this._internalStop(timeline);
                break;
            }
        }
    }

    /**
     * Stop all the currently playing animations.
     * @method PIXI.animate.Animator#stopAll
     * @static
     */
    static stopAll() {
        for (let i = this._timelines.length - 1; i >= 0; i--) {
            this._internalStop(this._timelines[i]);
        }
    }

    /**
     * Stop the animation
     * @method PIXI.animate.Animator#_internalStop
     * @private
     * @static
     * @param {PIXI.animate.AnimatorTimeline} timeline Timeline to stop.
     */
    static _internalStop(timeline) {
        this._timelines.splice(this._timelines.indexOf(timeline), 1);
        timeline.instance.stop();
        timeline.destroy();
    }
}

module.exports = Animator;