1 
  2 /**
  3  * @class Tween类提供缓动功能。
  4  * @param {Object} target 缓动对象。
  5  * @param {Object} newProps 对象缓动的目标属性集合。
  6  * @param {Object} params 缓动参数。可包含Tween类所有可写属性。
  7  * @module hilo/util/Tween
  8  * @requires hilo/core/Class
  9  * @property {Object} target 缓动目标。只读属性。
 10  * @property {Number} time 缓动总时长。单位毫秒。
 11  * @property {Number} delay 缓动延迟时间。单位毫秒。
 12  * @property {Boolean} paused 缓动是否暂停。默认为false。
 13  * @property {Boolean} loop 缓动是否循环。默认为false。
 14  * @property {Boolean} reverse 缓动是否反转播放。默认为false。
 15  * @property {Number} interval 缓动间隔。默认为0。
 16  * @property {Function} ease 缓动变化函数。默认为null。
 17  * @property {Tween} next 下一个缓动变化对象。默认为null。
 18  * @property {Function} onUpdate 缓动更新回调函数。默认为null。
 19  * @property {Function} onComplete 缓动结束回调函数。默认为null。
 20  */
 21 var Tween = (function(){
 22 
 23 return Class.create(/** @lends Tween.prototype */{
 24     constructor: function Tween(target, newProps, params){
 25         var me = this;
 26         me.target = target;
 27 
 28         me._oldProps = {};
 29         me._newProps = {};
 30         me._deltaProps = {};
 31         me._startTime = 0;
 32         me._lastTime = 0;
 33         me._pausedTime = 0;
 34         me._pausedStartTime = 0;
 35         me._reverseFlag = 1;
 36         me._frameTotal = 0;
 37         me._frameCount = 0;
 38 
 39         for(var p in newProps){       
 40             var oldVal = target[p], newVal = newProps[p];
 41             if(oldVal !== undefined){
 42                 if(typeof oldVal == 'number' && typeof newVal == 'number'){
 43                     me._oldProps[p] = oldVal;
 44                     me._newProps[p] = newVal;
 45                     me._deltaProps[p] = newVal - oldVal;
 46                 }
 47             }
 48         }
 49 
 50         for(var p in params){
 51             me[p] = params[p];
 52         }
 53     },
 54 
 55     target: null,
 56     time: 0,
 57     delay: 0,
 58     paused: false,
 59     loop: false,
 60     reverse: false,
 61     interval: 0,
 62     ease: null,
 63     next: null,
 64 
 65     onUpdate: null,
 66     onComplete: null,
 67 
 68     /**
 69      * 设置缓动对象的初始和目标属性。
 70      * @param oldProps 缓动对象的初始属性。
 71      * @param newProps 缓动对象的目标属性。
 72      */
 73     setProps: function(oldProps, newProps){
 74         for(var p in oldProps){
 75             this.target[p] = this._oldProps[p] = oldProps[p];
 76         }
 77         for(var p in newProps){
 78             this._newProps[p] = newProps[p];
 79             this._deltaProps[p] = newProps[p] - this.target[p];
 80         }
 81     },
 82 
 83     /**
 84      * 初始化Tween类。
 85      * @private
 86      */
 87     _init: function(){
 88         this._startTime = Date.now() + this.delay;
 89         this._pausedTime = 0;
 90         if(this.interval > 0) this._frameTotal = Math.round(this.time / this.interval);
 91         Tween.add(this);
 92     },
 93 
 94     /**
 95      * 启动缓动动画的播放。
 96      */
 97     start: function(){   
 98         this._init();
 99         this.paused = false;
100     },
101 
102     /**
103      * 停止缓动动画的播放。
104      */
105     stop: function(){
106         Tween.remove(this);
107     },
108 
109     /**
110      * 暂停缓动动画的播放。
111      */
112     pause: function(){   
113         this.paused = true;
114         this._pausedStartTime = Date.now();
115     },
116 
117     /**
118      * 恢复缓动动画的播放。
119      */
120     resume: function(){   
121         this.paused = false;
122         this._pausedTime += Date.now() - this._pausedStartTime;
123     },
124 
125     /**
126      * Tween类的内部更新方法。
127      * @private
128      */
129     _update: function(){
130         if(this.paused) return;
131         var now = Date.now();
132         var elapsed = now - this._startTime - this._pausedTime;
133         if(elapsed < 0) return;
134         
135         this._lastTime = now;
136         
137         var ratio = this._frameTotal > 0 ? (++this._frameCount / this._frameTotal) : (elapsed / this.time);
138         if(ratio > 1) ratio = 1;
139         var value = this.ease ? this.ease(ratio) : ratio;
140 
141         for(var p in this._oldProps){
142             this.target[p] = this._oldProps[p] + this._deltaProps[p] * this._reverseFlag * value;
143         }
144         
145         if(this.onUpdate) this.onUpdate(value);
146 
147         if(ratio >= 1){   
148             if(this.reverse){
149                 var tmp = this._oldProps;
150                 this._oldProps = this._newProps;
151                 this._newProps = tmp;
152                 this._startTime = Date.now();
153                 this._frameCount = 0;
154                 this._reverseFlag *= -1;
155             }else if(this.loop){
156                 for(var p in this._oldProps) this.target[p] = this._oldProps[p];
157                 this._startTime = Date.now();
158                 this._frameCount = 0;
159             }else{
160                 Tween.remove(this);
161                 var next = this.next, nextTween;
162                 if(next){
163                     if(next instanceof Tween){
164                         nextTween = next;
165                         next = null;
166                     }else{
167                         nextTween = next.shift();
168                     }
169                     if(nextTween){
170                         nextTween.next = next;
171                         nextTween.start();
172                     }
173                 }
174             }
175 
176             if(this.onComplete) this.onComplete(this);
177             if(this.reverse && !this.loop) this.reverse = false;
178         }
179     },
180 
181     Statics: /** @lends Tween */ {
182         /**
183          * @private
184          */
185         _tweens: [],
186 
187         /**
188          * 更新所有Tween实例。
189          * @returns {Object} Tween。
190          */
191         tick: function(){
192             var tweens = this._tweens, i = tweens.length;
193             while(--i >= 0) tweens[i]._update();
194             return this;
195         },
196 
197         /**
198          * 添加Tween实例。
199          * @param {Tween} tween 要添加的Tween对象。
200          * @returns {Object} Tween。
201          */
202         add: function(tween){
203             if(this._tweens.indexOf(tween) == -1) this._tweens.push(tween);
204             return this;
205         },
206 
207         /**
208          * 删除Tween实例。
209          * @param {Tween} tween 要删除的Tween对象。
210          * @returns {Object} Tween。
211          */
212         remove: function(tween){
213             if(tween){
214                 var tweens = this._tweens;
215                 var index = tweens.indexOf(tween);
216                 if(index > -1) tweens.splice(index, 1);
217             }
218             return this;
219         },
220 
221         /**
222          * 删除所有Tween实例。
223          * @returns {Object} Tween。
224          */
225         removeAll: function(){
226             this._tweens.length = 0;
227             return this;
228         },
229 
230         /**
231          * 创建一个缓动动画,让目标对象从当前属性变换到目标属性。
232          * @param target 缓动目标对象。
233          * @param toProps 缓动目标对象的目标属性。
234          * @param params 缓动动画的参数。
235          * @returns {Tween} 一个Tween实例对象。
236          */
237         to: function(target, toProps, params){
238             var tween = new Tween(target, toProps, params);
239             tween._init();
240             return tween;
241         },
242 
243         /**
244          * 创建一个缓动动画,让目标对象从指定的起始属性变换到当前属性。
245          * @param target 缓动目标对象。
246          * @param toProps 缓动目标对象的起始属性。
247          * @param params 缓动动画的参数。
248          * @returns {Tween} 一个Tween实例对象。
249          */
250         from: function(target, fromProps, params){
251             var tween = new Tween(target, fromProps, params);
252             var tmp = tween._oldProps;
253             tween._oldProps = tween._newProps;
254             tween._newProps = tmp;
255             tween._reverseFlag = -1;
256 
257             for(var p in tween._oldProps) target[p] = tween._oldProps[p];
258 
259             tween._init();
260             return tween;
261         }
262     }
263 
264 });
265 
266 })();