Source: animation/keyframeanimation.js

goog.provide('lime.animation.KeyframeAnimation');


goog.require('goog.events');
goog.require('lime.Sprite');
goog.require('lime.animation.Animation');

/**
 * Keyframe Animation object.
 * Keyframe Animation contains images that are changed on targets.
 * Similar to GIF effect.
 * @constructor
 * @extends lime.animation.Animation
 */
lime.animation.KeyframeAnimation = function() {
    lime.animation.Animation.call(this);

    /**
     * Array of frame images
     * @type {Array.<Image>}
     * @private
     */
    this.frames_ = [];

    /**
     * Number of frame images loaded
     * @type {number}
     * @private
     */
    this.numFramesLoaded_ = 0;

    /**
     * Have all the frames been loaded?
     * @type {boolean}
     * @private
     */
    //this.framesLoaded_ = false;

    /**
     * Animation is using Background Canvas
     * to make the changes. Faster if there are many targets
     * but only supported on Webkit.
     * @type {boolean}
     * @private
     */
    this.usesBackgroundCanvas_ = false;

    /**
     * Current frame index of the playhead
     * @type {number}
     * @private
     */
    this.currentFrame_ = -1;

    /**
     * Delay in seconds between frames
     * @type {number}
     */
    this.delay = 1 / 15;

    /**
     * Should the animation keep looping or stop when frame animation done.
     * Setting loopoing to true, this is deafault behaviour.
     * @type {boolean}
     */
    this.looping = true;
};
goog.inherits(lime.animation.KeyframeAnimation, lime.animation.Animation);


/** @inheritDoc */
lime.animation.KeyframeAnimation.prototype.scope = 'keyframe';

/**
 * Returns the delay in seconds between frames.
 * @return {number} Delay between frames.
 */
lime.animation.KeyframeAnimation.prototype.getDelay = function() {
    return this.delay;
};

/**
 * Set the delay between frames to specific value.
 * @param {number} value New delay value.
 * @return {lime.animation.KeyframeAnimation} object itself.
 */
lime.animation.KeyframeAnimation.prototype.setDelay = function(value) {
    this.delay = value;
    return this;
};

/**
 * Set array of frames to be used in the animation
 * @param {Array.<string>} frames Paths to frame images.
 * @return {lime.animation.KeyframeAnimation} object itself.
 */
lime.animation.KeyframeAnimation.prototype.setFrames = function(frames) {
    this.frames_ = [];
    this.numFramesLoaded_ = 0;
    this.currentFrame_ = -1;

    for (var i = 0; i < frames.length; i++) {
        this.addFrame(lime.fill.parse([frames[i]]));
    }
    return this;
};

/**
 * Set the keyframe animation to keep loopoing or just play the animation once.
 * @param {boolean} looping Keep looping or not.
 * @return {lime.animation.KeyframeAnimation} object itself.
 */
lime.animation.KeyframeAnimation.prototype.setLooping = function(looping) {
    this.looping = looping;
    return this;
};

/**
 * Add frame to the current animation
 * @param {string|lime.fill.Fill} frame Path to frame image.
 * @return {lime.animation.KeyframeAnimation} object itself.
 */
lime.animation.KeyframeAnimation.prototype.addFrame = function(frame) {
    this.framesLoaded_ = false;

    var fill = lime.fill.parse(goog.array.toArray(arguments));

    if (fill.id == 'image' && !fill.isLoaded()) {
        goog.events.listen(fill, goog.events.EventType.LOAD,
            this.frameLoadedHandler_, false, this);
    } else if (fill.id == 'frame' && !frame.isProcessed()) {
        goog.events.listen(fill, 'processed', this.frameLoadedHandler_, false, this);
    } else {
        this.numFramesLoaded_++;
    }
    this.frames_.push(fill);
    return this;
};

/**
 * Handler to be called on every loaded frame image
 * @private
 */
lime.animation.KeyframeAnimation.prototype.frameLoadedHandler_ = function() {
    this.numFramesLoaded_++;
};



/**
 * @inheritDoc
 */
lime.animation.KeyframeAnimation.prototype.play = function() {

    this.lastChangeTime_ = 0;

    lime.animation.Animation.prototype.play.call(this);
};

/**
 * @inheritDoc
 */
lime.animation.KeyframeAnimation.prototype.updateAll = function(t, targets) {
    var dt = this.dt_,
        delay_msec = Math.round(this.delay * 1000),
        nextImage = null,
        i = targets.length,
        looping = this.looping,
        validframe;

    while (--i >= 0) {
        this.getTargetProp(targets[i]);
    }

    this.lastChangeTime_ += dt;
    var nextFrame = this.currentFrame_ + 1;
    if (this.lastChangeTime_ > delay_msec) {
        if (nextFrame < this.frames_.length) {
            validframe = true;
        } else if (looping && nextFrame >= this.frames_.length) {
            validframe = true;
            nextFrame = 0;
        } else {
            validframe = false;
        }
        if (validframe) {
            nextImage = this.frames_[nextFrame];

            i = targets.length;
            if (i > 0) {

                // Todo: make CSS Canvas optional
                /* if (!this.usesBackgroundCanvas_ &&
                 goog.isFunction(document.getCSSCanvasContext)) {

                 this.bgSprite = new lime.Sprite;
                 this.bgSprite.setRenderMode(lime.RenderMode.CSS_CANVAS);
                 this.bgSprite.setQuality(this.targets[0].getQuality());
                 this.usesBackgroundCanvas_ = true;

                 }
                if (this.usesBackgroundCanvas_) {
                    this.bgSprite.setFill(nextImage);
                     while (--i >= 0) {
                     this.targets[i].setRenderMode(
                     lime.RenderMode.BACKGROUND_CANVAS);
                     this.targets[i].setFill(this.bgSprite);
                     }
                }
                else {
                    */
                while (--i >= 0) {
                    this.targets[i].setFill(nextImage);

                }
                //}
            }

            this.currentFrame_ = nextFrame;

            this.lastChangeTime_ -= delay_msec;
            this.lastChangeTime_ %= delay_msec;
        }
    }

    if (!looping) {
        return nextFrame / this.frames_.length;
    } else {
        return 0;
    }
};