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 })();