1 /** 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2016 DeNA Co., Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 /// <reference path="base.js"/> 26 /// <reference path="display_object.js"/> 27 /// <reference path="sprite_sheet.js"/> 28 /// <reference path="bounding_box.js"/> 29 /// <reference path="tick_event.js"/> 30 31 /** 32 * A class that displays a frame or sequence of frames (i.e. an animation) in a 33 * createjs.SpriteSheet object. 34 * @param {createjs.SpriteSheet} spriteSheet 35 * @param {(string|number)=} opt_frameOrAnimation 36 * @extends {createjs.DisplayObject} 37 * @constructor 38 */ 39 createjs.Sprite = function(spriteSheet, opt_frameOrAnimation) { 40 createjs.DisplayObject.call(this); 41 42 if (spriteSheet != null) { 43 /** 44 * The SpriteSheet instance exported to applications. 45 * @type {createjs.SpriteSheet} 46 */ 47 this['spriteSheet'] = spriteSheet; 48 49 /** 50 * The SpriteSheet instance being played. 51 * @type {createjs.SpriteSheet} 52 * @private 53 */ 54 this.spriteSheet_ = spriteSheet; 55 56 /** 57 * The number of frames to advance. 58 * @type {number} 59 * @private 60 */ 61 this.framerate_ = spriteSheet.framerate; 62 63 if (opt_frameOrAnimation != null) { 64 this.gotoAndPlay(opt_frameOrAnimation || 0); 65 } 66 } 67 }; 68 createjs.inherits('Sprite', createjs.Sprite, createjs.DisplayObject); 69 70 /** 71 * The name of the animation being played. 72 * @type {string} 73 * @private 74 */ 75 createjs.Sprite.prototype.currentAnimation = ''; 76 77 /** 78 * The frame index within the animation being played. 79 * @type {number} 80 * @private 81 */ 82 createjs.Sprite.prototype.currentAnimationFrame = 0; 83 84 /** 85 * The last time when this sprite has been updated. 86 * @type {number} 87 * @private 88 */ 89 createjs.Sprite.prototype.lastTime_ = 0; 90 91 /** 92 * @type {createjs.SpriteSheet.Animation} 93 * @private 94 */ 95 createjs.Sprite.prototype.animation_ = null; 96 97 /** 98 * Updates the bounding box of this sprite and sets its dirty flag when this 99 * sprite has its frame updated. 100 * @param {number} currentFrame 101 * @private 102 */ 103 createjs.Sprite.prototype.updateShape_ = function(currentFrame) { 104 /// <param type="number" name="currentFrame"/> 105 if (currentFrame != this.getCurrentFrame()) { 106 var frame = this.spriteSheet_.getFrame(this.getCurrentFrame()); 107 if (!frame) { 108 return; 109 } 110 var minX = -frame.regX; 111 var minY = -frame.regY; 112 var maxX = minX + frame.rect.width; 113 var maxY = minY + frame.rect.height; 114 this.setBoundingBox(minX, minY, maxX, maxY); 115 this.setDirty(createjs.DisplayObject.DIRTY_SHAPE); 116 } 117 }; 118 119 /** 120 * Normalizes the current animation frame, advancing animations and dispatching 121 * callbacks as appropriate. 122 * @private 123 */ 124 createjs.Sprite.prototype.normalizeAnimation_ = function() { 125 var TYPE = 'animationend'; 126 var hasListener = this.hasListener(TYPE); 127 var animation = this.animation_; 128 var frame = createjs.floor(this.currentAnimationFrame); 129 var length = animation.getFrameLength(); 130 while (frame >= length) { 131 var next = animation.getNext(); 132 if (hasListener) { 133 var event = new createjs.AnimationEvent( 134 TYPE, false, false, animation.getName(), next); 135 this.dispatchRawEvent(event); 136 } 137 if (!next) { 138 this.setIsPaused(true); 139 frame = length - 1; 140 break; 141 } 142 animation = this.spriteSheet_.getAnimation(next); 143 frame -= length; 144 length = animation.getFrameLength(); 145 } 146 this.currentAnimationFrame = frame; 147 this.setCurrentFrame(animation.getFrame(frame)); 148 this.animation_ = animation; 149 }; 150 151 /** 152 * Normalizes the current frame, advancing animations and dispatching callbacks 153 * as appropriate. 154 * @private 155 */ 156 createjs.Sprite.prototype.normalizeFrame_ = function() { 157 var TYPE = 'animationend'; 158 var hasListener = this.hasListener(TYPE); 159 var frame = createjs.floor(this.getCurrentFrame()); 160 var length = this.spriteSheet_.getFrameLength(); 161 while (frame >= length) { 162 if (hasListener) { 163 var event = new createjs.AnimationEvent(TYPE, false, false, '', ''); 164 this.dispatchRawEvent(event); 165 } 166 frame -= length; 167 } 168 this.setCurrentFrame(frame); 169 }; 170 171 /** 172 * Moves the play offset to the specified frame number or the beginning of the 173 * specified animation. 174 * @param {boolean} paused 175 * @param {string|number} frameOrAnimation 176 * @private 177 */ 178 createjs.Sprite.prototype.goto_ = function(paused, frameOrAnimation) { 179 this.setIsPaused(paused); 180 var currentFrame = this.getCurrentFrame(); 181 if (createjs.isString(frameOrAnimation)) { 182 var name = createjs.getString(frameOrAnimation); 183 if (this.currentAnimation != name) { 184 var animation = this.spriteSheet_.getAnimation(name); 185 createjs.assert(!!animation); 186 this.animation_ = animation; 187 this.currentAnimation = name; 188 this.setCurrentFrame(animation.getFrame(0)); 189 } 190 this.currentAnimationFrame = 0; 191 } else { 192 var frame = createjs.getNumber(frameOrAnimation); 193 this.currentAnimationFrame = 0; 194 this.currentAnimation = ''; 195 this.animation_ = null; 196 this.setCurrentFrame(frame); 197 this.normalizeFrame_(); 198 } 199 this.updateShape_(currentFrame); 200 }; 201 202 /** 203 * Starts playing (unpause) the current animation. The Sprite will be paused if 204 * either the Sprite.stop() method or the Sprite.gotoAndStop() method is called. 205 * Single frame animations will remain unchanged. 206 * @const 207 */ 208 createjs.Sprite.prototype.play = function() { 209 this.setIsPaused(false); 210 this.setDirty(createjs.DisplayObject.DIRTY_ALL); 211 }; 212 213 /** 214 * Stops playing a running animation. The Sprite will be playing if the 215 * Sprite.gotoAndPlay() method is called. Note that calling the 216 * Sprite.gotoAndPlay() method or the Sprite.play() method will resume playback. 217 * @const 218 */ 219 createjs.Sprite.prototype.stop = function() { 220 this.setIsPaused(true); 221 }; 222 223 /** 224 * Sets paused to false and plays the specified animation name, named frame, or 225 * frame number. 226 * @param {string|number} value 227 * @const 228 */ 229 createjs.Sprite.prototype.gotoAndPlay = function(value) { 230 this.goto_(false, value); 231 this.setDirty(createjs.DisplayObject.DIRTY_ALL); 232 }; 233 234 /** 235 * Sets paused to true and seeks to the specified animation name, named frame, 236 * or frame number. 237 * @param {string|number} value 238 * @const 239 */ 240 createjs.Sprite.prototype.gotoAndStop = function(value) { 241 this.goto_(true, value); 242 }; 243 244 /** 245 * Advances the play position. This occurs automatically each tick by default. 246 * @param {number=} opt_time 247 * @const 248 */ 249 createjs.Sprite.prototype.advance = function(opt_time) { 250 var time = 1; 251 if (opt_time && this.framerate_) { 252 time = opt_time * this.framerate_ / 1000; 253 } 254 var currentFrame = this.getCurrentFrame(); 255 if (this.animation_) { 256 this.currentAnimationFrame += time * this.animation_.getSpeed(); 257 this.normalizeAnimation_(); 258 } else { 259 this.setCurrentFrame(currentFrame + time); 260 this.normalizeFrame_(); 261 } 262 this.updateShape_(currentFrame); 263 }; 264 265 /** @override */ 266 createjs.Sprite.prototype.isVisible = function() { 267 if (!createjs.Sprite.superClass_.isVisible.call(this)) { 268 return false; 269 } 270 return this.spriteSheet_.isComplete(); 271 }; 272 273 /** @override */ 274 createjs.Sprite.prototype.removeAllChildren = function(opt_destroy) { 275 this['spriteSheet'] = null; 276 this.spriteSheet_ = null; 277 }; 278 279 /** @override */ 280 createjs.Sprite.prototype.layout = 281 function(renderer, parent, dirty, time, draw) { 282 /// <param type="createjs.Renderer" name="renderer"/> 283 /// <param type="createjs.DisplayObject" name="parent"/> 284 /// <param type="number" name="dirty"/> 285 /// <param type="number" name="time"/> 286 /// <param type="number" name="draw"/> 287 if (!this.spriteSheet_.isComplete()) { 288 return 0; 289 } 290 if (!this.isPaused()) { 291 var delta = time - this.lastTime_; 292 if (delta > 0) { 293 this.advance(time); 294 } 295 this.lastTime_ = time; 296 } 297 return createjs.Sprite.superClass_.layout.call( 298 this, renderer, parent, dirty, time, draw); 299 }; 300 301 /** @override */ 302 createjs.Sprite.prototype.paintObject = function(renderer) { 303 var frame = 304 this.spriteSheet_.getFrame(createjs.floor(this.getCurrentFrame())); 305 createjs.assert(!!frame); 306 var rect = frame.rect; 307 renderer.drawPartial( 308 frame.image, 309 rect.x, rect.y, rect.width, rect.height, 310 -frame.regX, -frame.regY, rect.width, rect.height); 311 }; 312 313 // Export the createjs.Sprite object to the global namespace. 314 createjs.exportObject('createjs.Sprite', createjs.Sprite, { 315 // createjs.Sprite methods 316 'play': createjs.Sprite.prototype.play, 317 'stop': createjs.Sprite.prototype.stop, 318 'gotoAndPlay': createjs.Sprite.prototype.gotoAndPlay, 319 'gotoAndStop': createjs.Sprite.prototype.gotoAndStop, 320 'advance': createjs.Sprite.prototype.advance 321 322 // createjs.DisplayObject methods 323 324 // createjs.EventDispatcher methods 325 326 // createjs.Object methods. 327 }); 328