1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @language=en 9 * <iframe src='../../../examples/Sprite.html?noHeader' width = '550' height = '400' scrolling='no'></iframe> 10 * <br/> 11 * @class Sprite animation class. 12 * @augments View 13 * @module hilo/view/Sprite 14 * @requires hilo/core/Hilo 15 * @requires hilo/core/Class 16 * @requires hilo/view/View 17 * @requires hilo/view/Drawable 18 * @param properties Properties parameters for creating object, include all writable properties of this class, also include: 19 * <ul> 20 * <li><b>frames</b> - Sprite animation frames data object.</li> 21 * </ul> 22 * @property {number} currentFrame Current showing frame index, range from 0, readoly! 23 * @property {boolean} paused Is sprite paused, default value is false. 24 * @property {boolean} loop Is sprite play in loop, default value is false. 25 * @property {boolean} timeBased Is sprite animate base on time, default value is false (base on frame). 26 * @property {number} interval Interval between sprite animation frames. If timeBased is true, measured in ms, otherwise, measured in frames. 27 */ 28 var Sprite = Class.create(/** @lends Sprite.prototype */{ 29 Extends: View, 30 constructor: function(properties){ 31 properties = properties || {}; 32 this.id = this.id || properties.id || Hilo.getUid("Sprite"); 33 Sprite.superclass.constructor.call(this, properties); 34 35 this._frames = []; 36 this._frameNames = {}; 37 this.drawable = new Drawable(); 38 if(properties.frames) this.addFrame(properties.frames); 39 }, 40 41 _frames: null, //所有帧的集合 Collection of all frames 42 _frameNames: null, //带名字name的帧的集合 Collection of frames that with name 43 _frameElapsed: 0, //当前帧持续的时间或帧数 Elapsed time of current frame. 44 _firstRender: true, //标记是否是第一次渲染 Is the first render. 45 46 paused: false, 47 loop: true, 48 timeBased: false, 49 interval: 1, 50 currentFrame: 0, //当前帧的索引 Index of current frame 51 52 /** 53 * @language=en 54 * Return the total amount of sprite animation frames. 55 * @returns {Uint} The total amount of frames. 56 */ 57 getNumFrames: function(){ 58 return this._frames ? this._frames.length : 0; 59 }, 60 61 /** 62 * @language=en 63 * Add frame into sprite. 64 * @param {Object} frame Frames to add into. 65 * @param {Int} startIndex The index to start adding frame, if is not given, add at the end of sprite. 66 * @returns {Sprite} Sprite itself. 67 */ 68 addFrame: function(frame, startIndex){ 69 var start = startIndex != null ? startIndex : this._frames.length; 70 if(frame instanceof Array){ 71 for(var i = 0, len = frame.length; i < len; i++){ 72 this.setFrame(frame[i], start + i); 73 } 74 }else{ 75 this.setFrame(frame, start); 76 } 77 return this; 78 }, 79 80 /** 81 * @language=en 82 * Set the frame on the given index. 83 * @param {Object} frame The frame data to set on that index. 84 * @param {Int} index Index of the frame to set. 85 * @returns {Sprite} Sprite itself. 86 */ 87 setFrame: function(frame, index){ 88 var frames = this._frames, 89 total = frames.length; 90 index = index < 0 ? 0 : index > total ? total : index; 91 frames[index] = frame; 92 if(frame.name) this._frameNames[frame.name] = frame; 93 if(index == 0 && !this.width || !this.height){ 94 this.width = frame.rect[2]; 95 this.height = frame.rect[3]; 96 } 97 return this; 98 }, 99 100 /** 101 * @language=en 102 * Get the frame of given parameter from sprite. 103 * @param {Object} indexOrName The index or name of the frame. 104 * @returns {Object} The sprite object. 105 */ 106 getFrame: function(indexOrName){ 107 if(typeof indexOrName === 'number'){ 108 var frames = this._frames; 109 if(indexOrName < 0 || indexOrName >= frames.length) return null; 110 return frames[indexOrName]; 111 } 112 return this._frameNames[indexOrName]; 113 }, 114 115 /** 116 * @language=en 117 * Get frame index from sprite. 118 * @param {Object} frameValue Index or name of the frame. 119 * @returns {Object} Sprite frame object. 120 */ 121 getFrameIndex: function(frameValue){ 122 var frames = this._frames, 123 total = frames.length, 124 index = -1; 125 if(typeof frameValue === 'number'){ 126 index = frameValue; 127 }else{ 128 var frame = typeof frameValue === 'string' ? this._frameNames[frameValue] : frameValue; 129 if(frame){ 130 for(var i = 0; i < total; i++){ 131 if(frame === frames[i]){ 132 index = i; 133 break; 134 } 135 } 136 } 137 } 138 return index; 139 }, 140 141 /** 142 * @language=en 143 * Play sprite. 144 * @returns {Sprite} The Sprite object. 145 */ 146 play: function(){ 147 this.paused = false; 148 return this; 149 }, 150 151 /** 152 * @language=en 153 * Pause playing sprite. 154 * @returns {Sprite} The Sprite object. 155 */ 156 stop: function(){ 157 this.paused = true; 158 return this; 159 }, 160 161 /** 162 * @language=en 163 * Jump to an assigned frame. 164 * @param {Object} indexOrName Index or name of an frame to jump to. 165 * @param {Boolean} pause Does pause after jumping to the new index. 166 * @returns {Sprite} The Sprite object. 167 */ 168 goto: function(indexOrName, pause){ 169 var total = this._frames.length, 170 index = this.getFrameIndex(indexOrName); 171 172 this.currentFrame = index < 0 ? 0 : index >= total ? total - 1 : index; 173 this.paused = pause; 174 this._firstRender = true; 175 return this; 176 }, 177 178 /** 179 * @language=en 180 * Render function. 181 * @private 182 */ 183 _render: function(renderer, delta){ 184 var lastFrameIndex = this.currentFrame, frameIndex; 185 186 if(this._firstRender){ 187 frameIndex = lastFrameIndex; 188 this._firstRender = false; 189 }else{ 190 frameIndex = this._nextFrame(delta); 191 } 192 193 if(frameIndex != lastFrameIndex){ 194 this.currentFrame = frameIndex; 195 var callback = this._frames[frameIndex].callback; 196 callback && callback.call(this); 197 } 198 199 //NOTE: it will be deprecated, don't use it. 200 if(this.onEnterFrame) this.onEnterFrame(frameIndex); 201 202 this.drawable.init(this._frames[frameIndex]); 203 Sprite.superclass._render.call(this, renderer, delta); 204 }, 205 206 /** 207 * @language=en 208 * @private 209 */ 210 _nextFrame: function(delta){ 211 var frames = this._frames, 212 total = frames.length, 213 frameIndex = this.currentFrame, 214 frame = frames[frameIndex], 215 duration = frame.duration || this.interval, 216 elapsed = this._frameElapsed; 217 218 //calculate the current frame elapsed frames/time 219 var value = (frameIndex == 0 && !this.drawable) ? 0 : elapsed + (this.timeBased ? delta : 1); 220 elapsed = this._frameElapsed = value < duration ? value : 0; 221 222 if(frame.stop || !this.loop && frameIndex >= total - 1){ 223 this.stop(); 224 } 225 226 if(!this.paused && elapsed == 0){ 227 if(frame.next != null){ 228 //jump to the specified frame 229 frameIndex = this.getFrameIndex(frame.next); 230 }else if(frameIndex >= total - 1){ 231 //at the end of the frames, go back to first frame 232 frameIndex = 0; 233 }else if(this.drawable){ 234 //normal go forward to next frame 235 frameIndex++; 236 } 237 } 238 239 return frameIndex; 240 }, 241 242 /** 243 * @language=en 244 * Set a callback on an assigned frame. Every time assigned frame is played, invoke the callback function. If callback is empty, callback function will be removed. 245 * @param {Int|String} frame Index or name of the assigned frame. 246 * @param {Function} callback Callback function. 247 * @returns {Sprite} The Sprite object. 248 */ 249 setFrameCallback: function(frame, callback){ 250 frame = this.getFrame(frame); 251 if(frame) frame.callback = callback; 252 return this; 253 }, 254 255 /** 256 * @language=en 257 * Callback function on when sprite enter a new frame. default value is null. Note: this function is obsolete, use addFrameCallback funciton instead. 258 * @type Function 259 * @deprecated 260 */ 261 onEnterFrame: null 262 263 }); 264