1 
  2 /**
  3  * @class 动画精灵类。
  4  * @augments View
  5  * @module hilo/view/Sprite
  6  * @requires hilo/core/Hilo
  7  * @requires hilo/core/Class
  8  * @requires hilo/view/View
  9  * @requires hilo/view/Drawable
 10  * @param properties 创建对象的属性参数。可包含此类所有可写属性。此外还包括:
 11  * <ul>
 12  * <li><b>frames</b> - 精灵动画的帧数据对象。</li>
 13  * </ul>
 14  * @property {number} currentFrame 当前播放帧的索引。从0开始。
 15  * @property {number} numFrames 精灵动画的总帧数。
 16  * @property {boolean} paused 判断精灵是否暂停。默认为false。
 17  * @property {boolean} loop 判断精灵是否可以循环播放。默认为true。
 18  * @property {boolean} timeBased 指定精灵动画是否是以时间为基准。默认为false,即以帧为基准。
 19  * @property {number} interval 精灵动画的帧间隔。如果timeBased为true,则单位为毫秒,否则为帧数。
 20  */
 21 var Sprite = Class.create(/** @lends Sprite.prototype */{
 22     Extends: View,
 23     constructor: function Sprite(properties){
 24         properties = properties || {};
 25         this.id = this.id || properties.id || Hilo.getUid("Sprite");
 26         Sprite.superclass.constructor.call(this, properties);
 27 
 28         this._frames = [];
 29         this._frameNames = {};
 30         this.drawable = new Drawable();
 31         if(properties.frames) this.addFrame(properties.frames);
 32     },
 33 
 34     _frames: null, //所有帧的集合
 35     _frameNames: null, //带名字name的帧的集合
 36     _frameElapsed: 0, //当前帧持续的时间或帧数
 37     _currentFrame: 0, //当前帧的索引
 38 
 39     paused: false,
 40     loop: true,
 41     timeBased: false,
 42     interval: 1,
 43     currentFrame: {
 44         get: function(){
 45             return this._currentFrame;
 46         }
 47     },
 48     numFrames: {
 49         get: function(){
 50             return this._frames ? this._frames.length : 0;
 51         }
 52     },
 53 
 54     /**
 55      * 往精灵动画序列中增加帧。
 56      * @param {Object} frame 要增加的精灵动画帧数据。
 57      * @returns {Sprite} Sprite对象本身。
 58      */
 59     addFrame: function(frame){
 60         var total = this._frames.length;
 61         if(frame instanceof Array){
 62             for(var i = 0, len = frame.length; i < len; i++){
 63                 this.setFrame(frame[i], total + i);
 64             }
 65         }else{
 66             this.setFrame(frame, total);
 67         }
 68         return this;
 69     },
 70 
 71     /**
 72      * 设置精灵动画序列指定索引位置的帧。
 73      * @param {Object} frame 要设置的精灵动画帧数据。
 74      * @param {Int} index 要设置的索引位置。
 75      * @returns {Sprite} Sprite对象本身。
 76      */
 77     setFrame: function(frame, index){
 78         var frames = this._frames,
 79             total = frames.length;
 80         index = index < 0 ? 0 : index > total ? total : index;
 81         frames[index] = frame;
 82         if(frame.name) this._frameNames[frame.name] = frame;
 83         if(index == 0 && !this.width || !this.height){
 84             this.width = frame.rect[2];
 85             this.height = frame.rect[3];
 86         }
 87         return this;
 88     },
 89 
 90     /**
 91      * 获取精灵动画序列中指定的帧。
 92      * @param {Object} indexOrName 要获取的帧的索引位置或别名。
 93      * @returns {Object} 精灵帧对象。
 94      */
 95     getFrame: function(indexOrName){
 96         if(typeof indexOrName === 'number'){
 97             var frames = this._frames;
 98             if(indexOrName < 0 || indexOrName >= frames.length) return null;
 99             return frames[indexOrName];
100         }
101         return this._frameNames[indexOrName];
102     },
103 
104     /**
105      * 获取精灵动画序列中指定帧的索引位置。
106      * @param {Object} frameValue 要获取的帧的索引位置或别名。
107      * @returns {Object} 精灵帧对象。
108      */
109     getFrameIndex: function(frameValue){
110         var frames = this._frames,
111             total = frames.length,
112             index = -1;
113         if(typeof frameValue === 'number'){   
114             index = frameValue;
115         }else{
116             var frame = typeof frameValue === 'string' ? this._frameNames[frameValue] : frameValue;
117             if(frame){
118                 for(var i = 0; i < total; i++){
119                     if(frame === frames[i]){
120                         index = i;
121                         break;
122                     }
123                 }
124             }
125         }
126         return index;
127     },
128 
129     /**
130      * 播放精灵动画。
131      * @returns {Sprite} Sprite对象本身。
132      */
133     play: function(){
134         this.paused = false;
135         return this;
136     },
137 
138     /**
139      * 暂停播放精灵动画。
140      * @returns {Sprite} Sprite对象本身。
141      */
142     stop: function(){
143         this.paused = true;
144         return this;
145     },
146 
147     /**
148      * 跳转精灵动画到指定的帧。
149      * @param {Object} indexOrName 要跳转的帧的索引位置或别名。
150      * @param {Boolean} pause 指示跳转后是否暂停播放。
151      * @returns {Sprite} Sprite对象本身。
152      */
153     goto: function(indexOrName, pause){
154         var total = this._frames.length,
155             index = this.getFrameIndex(indexOrName);
156 
157         this._currentFrame = index < 0 ? 0 : index >= total ? total - 1 : index;
158         this.paused = pause;
159         return this;
160     },
161 
162     /**
163      * 渲染方法。
164      * @private
165      */
166     _render: function(renderer, delta){
167         var frameIndex = this._nextFrame(delta),
168             frame = this._frames[frameIndex];
169 
170         this.drawable.init(frame);
171         Sprite.superclass._render.call(this, renderer, delta);
172     },
173 
174     /**
175      * @private
176      */
177     _nextFrame: function(delta){
178         var frames = this._frames,
179             total = frames.length,
180             frameIndex = this._currentFrame, 
181             frame = frames[frameIndex],
182             duration = frame.duration || this.interval,
183             elapsed = this._frameElapsed;
184 
185         //calculate the current frame elapsed frames/time
186         var value = (frameIndex == 0 && !this.drawable) ? 0 : elapsed + (this.timeBased ? delta : 1);
187         elapsed = this._frameElapsed = value < duration ? value : 0;
188 
189         if(frame.stop || !this.loop && frameIndex >= total - 1){
190             this.stop();
191         }
192 
193         if(!this.paused && elapsed == 0){
194             if(frame.next != null){
195                 //jump to the specified frame
196                 frameIndex = this.getFrameIndex(frame.next);
197             }else if(frameIndex >= total - 1){
198                 //at the end of the frames, go back to first frame
199                 frameIndex = 0;
200             }else if(this.drawable){
201                 //normal go forward to next frame
202                 frameIndex++;
203             }
204             
205             this._currentFrame = frameIndex;
206         }
207 
208         if(this.onEnterFrame) this.onEnterFrame(frameIndex);
209         
210         return frameIndex;
211     },
212 
213     /**
214      * 精灵动画的播放头进入新帧时的回调方法。默认值为null。
215      * @type Function
216      */
217     onEnterFrame: null
218 
219 });