1 goog.provide('lime.animation.KeyframeAnimation');
  2 
  3 
  4 goog.require('goog.events');
  5 goog.require('lime.Sprite');
  6 goog.require('lime.animation.Animation');
  7 
  8 /**
  9  * Keyframe Animation object.
 10  * Keyframe Animation contains images that are changed on targets.
 11  * Similar to GIF effect.
 12  * @constructor
 13  * @extends lime.animation.Animation
 14  */
 15 lime.animation.KeyframeAnimation = function() {
 16     lime.animation.Animation.call(this);
 17 
 18     /**
 19      * Array of frame images
 20      * @type {Array.<Image>}
 21      * @private
 22      */
 23     this.frames_ = [];
 24 
 25     /**
 26      * Number of frame images loaded
 27      * @type {number}
 28      * @private
 29      */
 30     this.numFramesLoaded_ = 0;
 31 
 32     /**
 33      * Have all the frames been loaded?
 34      * @type {boolean}
 35      * @private
 36      */
 37     //this.framesLoaded_ = false;
 38 
 39     /**
 40      * Animation is using Background Canvas
 41      * to make the changes. Faster if there are many targets
 42      * but only supported on Webkit.
 43      * @type {boolean}
 44      * @private
 45      */
 46     this.usesBackgroundCanvas_ = false;
 47 
 48     /**
 49      * Current frame index of the playhead
 50      * @type {number}
 51      * @private
 52      */
 53     this.currentFrame_ = -1;
 54 
 55     /**
 56      * Delay in seconds between frames
 57      * @type {number}
 58      */
 59     this.delay = 1 / 15;
 60 
 61     /**
 62      * Should the animation keep looping or stop when frame animation done.
 63      * Setting loopoing to true, this is deafault behaviour.
 64      * @type {boolean}
 65      */
 66     this.looping = true;
 67 };
 68 goog.inherits(lime.animation.KeyframeAnimation, lime.animation.Animation);
 69 
 70 
 71 /** @inheritDoc */
 72 lime.animation.KeyframeAnimation.prototype.scope = 'keyframe';
 73 
 74 /**
 75  * Returns the delay in seconds between frames.
 76  * @return {number} Delay between frames.
 77  */
 78 lime.animation.KeyframeAnimation.prototype.getDelay = function() {
 79     return this.delay;
 80 };
 81 
 82 /**
 83  * Set the delay between frames to specific value.
 84  * @param {number} value New delay value.
 85  * @return {lime.animation.KeyframeAnimation} object itself.
 86  */
 87 lime.animation.KeyframeAnimation.prototype.setDelay = function(value) {
 88     this.delay = value;
 89     return this;
 90 };
 91 
 92 /**
 93  * Set array of frames to be used in the animation
 94  * @param {Array.<string>} frames Paths to frame images.
 95  * @return {lime.animation.KeyframeAnimation} object itself.
 96  */
 97 lime.animation.KeyframeAnimation.prototype.setFrames = function(frames) {
 98     this.frames_ = [];
 99     this.numFramesLoaded_ = 0;
100     this.currentFrame_ = -1;
101 
102     for (var i = 0; i < frames.length; i++) {
103         this.addFrame(lime.fill.parse([frames[i]]));
104     }
105     return this;
106 };
107 
108 /**
109  * Set the keyframe animation to keep loopoing or just play the animation once.
110  * @param {boolean} looping Keep looping or not.
111  * @return {lime.animation.KeyframeAnimation} object itself.
112  */
113 lime.animation.KeyframeAnimation.prototype.setLooping = function(looping) {
114     this.looping = looping;
115     return this;
116 };
117 
118 /**
119  * Add frame to the current animation
120  * @param {string} frame Path to frame image.
121  * @return {lime.animation.KeyframeAnimation} object itself.
122  */
123 lime.animation.KeyframeAnimation.prototype.addFrame = function(frame) {
124     this.framesLoaded_ = false;
125 
126     var fill = lime.fill.parse(goog.array.toArray(arguments));
127 
128     if (fill.id == 'image' && !fill.isLoaded()) {
129         goog.events.listen(fill, goog.events.EventType.LOAD,
130                 this.frameLoadedHandler_, false, this);
131     }
132     else if (fill.id == 'frame' && !frame.isProcessed()) {
133         goog.events.listen(fill, 'processed', this.frameLoadedHandler_, false, this);
134     }
135     else {
136         this.numFramesLoaded_++;
137     }
138     this.frames_.push(fill);
139     return this;
140 };
141 
142 /**
143  * Handler to be called on every loaded frame image
144  * @private
145  */
146 lime.animation.KeyframeAnimation.prototype.frameLoadedHandler_ = function() {
147     this.numFramesLoaded_++;
148 };
149 
150 
151 
152 /**
153  * @inheritDoc
154  */
155 lime.animation.KeyframeAnimation.prototype.play = function() {
156 
157    this.lastChangeTime_ = 0;
158 
159    lime.animation.Animation.prototype.play.call(this);
160 };
161 
162 /**
163  * @inheritDoc
164  */
165 lime.animation.KeyframeAnimation.prototype.updateAll = function(t,targets) {
166     var dt = this.dt_,
167         delay_msec = Math.round(this.delay * 1000),
168         nextImage = null,
169         i = targets.length,
170         looping = this.looping,
171         validframe;
172 
173     while (--i >= 0) {
174         this.getTargetProp(targets[i]);
175     }
176 
177     this.lastChangeTime_ += dt;
178     var nextFrame = this.currentFrame_ + 1;
179     if (this.lastChangeTime_ > delay_msec) {
180         if (nextFrame < this.frames_.length) {
181             validframe = true;
182         }else if (looping && nextFrame >= this.frames_.length) {
183             validframe = true;
184             nextFrame = 0;
185         }else {
186             validframe = false;
187         }
188         if (validframe) {
189             nextImage = this.frames_[nextFrame];
190 
191             i = targets.length;
192             if (i > 0) {
193 
194                 // Todo: make CSS Canvas optional
195                 /* if (!this.usesBackgroundCanvas_ &&
196                  goog.isFunction(document.getCSSCanvasContext)) {
197 
198                  this.bgSprite = new lime.Sprite;
199                  this.bgSprite.setRenderMode(lime.RenderMode.CSS_CANVAS);
200                  this.bgSprite.setQuality(this.targets[0].getQuality());
201                  this.usesBackgroundCanvas_ = true;
202 
203                  }
204                  */
205                 if (this.usesBackgroundCanvas_) {
206                     this.bgSprite.setFill(nextImage);
207                     /* while (--i >= 0) {
208                      this.targets[i].setRenderMode(
209                      lime.RenderMode.BACKGROUND_CANVAS);
210                      this.targets[i].setFill(this.bgSprite);
211                      }*/
212                 }
213                 else {
214                     while (--i >= 0) {
215                         this.targets[i].setFill(nextImage);
216 
217                     }
218                 }
219             }
220 
221             this.currentFrame_ = nextFrame;
222 
223             this.lastChangeTime_ -= delay_msec;
224             this.lastChangeTime_ %= delay_msec;
225         }
226     }
227 
228     if (!looping) {
229         return nextFrame / this.frames_.length;
230     }else {
231         return 0;
232     }
233 };
234