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