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