1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * <iframe src='../../../examples/Tween.html?noHeader' width = '550' height = '130' scrolling='no'></iframe> 9 * <br/> 10 * 使用示例: 11 * <pre> 12 * ticker.addTick(Hilo.Tween);//需要把Tween加到ticker里才能使用 13 * 14 * var view = new View({x:5, y:10}); 15 * Hilo.Tween.to(view, { 16 * x:100, 17 * y:20, 18 * alpha:0 19 * }, { 20 * duration:1000, 21 * delay:500, 22 * ease:Hilo.Ease.Quad.EaseIn, 23 * onComplete:function(){ 24 * console.log('complete'); 25 * } 26 * }); 27 * </pre> 28 * @class Tween类提供缓动功能。 29 * @param {Object} target 缓动对象。 30 * @param {Object} fromProps 对象缓动的起始属性集合。 31 * @param {Object} toProps 对象缓动的目标属性集合。 32 * @param {Object} params 缓动参数。可包含Tween类所有可写属性。 33 * @module hilo/tween/Tween 34 * @requires hilo/core/Class 35 * @property {Object} target 缓动目标。只读属性。 36 * @property {Int} duration 缓动总时长。单位毫秒。 37 * @property {Int} delay 缓动延迟时间。单位毫秒。 38 * @property {Boolean} paused 缓动是否暂停。默认为false。 39 * @property {Boolean} loop 缓动是否循环。默认为false。 40 * @property {Boolean} reverse 缓动是否反转播放。默认为false。 41 * @property {Int} repeat 缓动重复的次数。默认为0。 42 * @property {Int} repeatDelay 缓动重复的延迟时长。单位为毫秒。 43 * @property {Function} ease 缓动变化函数。默认为null。 44 * @property {Int} time 缓动已进行的时长。单位毫秒。只读属性。 45 * @property {Function} onStart 缓动开始回调函数。它接受1个参数:tween。默认值为null。 46 * @property {Function} onUpdate 缓动更新回调函数。它接受2个参数:ratio和tween。默认值为null。 47 * @property {Function} onComplete 缓动结束回调函数。它接受1个参数:tween。默认值为null。 48 */ 49 var Tween = (function(){ 50 51 function now(){ 52 return +new Date(); 53 } 54 55 return Class.create(/** @lends Tween.prototype */{ 56 constructor: function(target, fromProps, toProps, params){ 57 var me = this; 58 59 me.target = target; 60 me._startTime = 0; 61 me._seekTime = 0; 62 me._pausedTime = 0; 63 me._pausedStartTime = 0; 64 me._reverseFlag = 1; 65 me._repeatCount = 0; 66 67 //no fromProps if pass 3 arguments 68 if(arguments.length == 3){ 69 params = toProps; 70 toProps = fromProps; 71 fromProps = null; 72 } 73 74 for(var p in params) me[p] = params[p]; 75 me.setProps(fromProps, toProps); 76 77 //for old version compatiblity 78 if(!params.duration && params.time){ 79 me.duration = params.time || 0; 80 me.time = 0; 81 } 82 }, 83 84 target: null, 85 duration: 0, 86 delay: 0, 87 paused: false, 88 loop: false, 89 reverse: false, 90 repeat: 0, 91 repeatDelay: 0, 92 ease: null, 93 time: 0, //ready only 94 95 onStart: null, 96 onUpdate: null, 97 onComplete: null, 98 99 /** 100 * 设置缓动对象的初始和目标属性。 101 * @param {Object} fromProps 缓动对象的初始属性。 102 * @param {Object} toProps 缓动对象的目标属性。 103 * @returns {Tween} Tween变换本身。可用于链式调用。 104 */ 105 setProps: function(fromProps, toProps){ 106 var me = this, target = me.target, 107 propNames = fromProps || toProps, 108 from = me._fromProps = {}, to = me._toProps = {}; 109 110 fromProps = fromProps || target; 111 toProps = toProps || target; 112 113 for(var p in propNames){ 114 to[p] = toProps[p] || 0; 115 target[p] = from[p] = fromProps[p] || 0; 116 } 117 return me; 118 }, 119 120 /** 121 * 启动缓动动画的播放。 122 * @returns {Tween} Tween变换本身。可用于链式调用。 123 */ 124 start: function(){ 125 var me = this; 126 me._startTime = now() + me.delay; 127 me._seekTime = 0; 128 me._pausedTime = 0; 129 me.paused = false; 130 Tween.add(me); 131 return me; 132 }, 133 134 /** 135 * 停止缓动动画的播放。 136 * @returns {Tween} Tween变换本身。可用于链式调用。 137 */ 138 stop: function(){ 139 Tween.remove(this); 140 return this; 141 }, 142 143 /** 144 * 暂停缓动动画的播放。 145 * @returns {Tween} Tween变换本身。可用于链式调用。 146 */ 147 pause: function(){ 148 var me = this; 149 me.paused = true; 150 me._pausedStartTime = now(); 151 return me; 152 }, 153 154 /** 155 * 恢复缓动动画的播放。 156 * @returns {Tween} Tween变换本身。可用于链式调用。 157 */ 158 resume: function(){ 159 var me = this; 160 me.paused = false; 161 if(me._pausedStartTime) me._pausedTime += now() - me._pausedStartTime; 162 me._pausedStartTime = 0; 163 return me; 164 }, 165 166 /** 167 * 跳转Tween到指定的时间。 168 * @param {Number} time 指定要跳转的时间。取值范围为:0 - duraion。 169 * @param {Boolean} pause 是否暂停。 170 * @returns {Tween} Tween变换本身。可用于链式调用。 171 */ 172 seek: function(time, pause){ 173 var me = this, current = now(); 174 me._startTime = current; 175 me._seekTime = time; 176 me._pausedTime = 0; 177 if(pause !== undefined) me.paused = pause; 178 me._update(current, true); 179 Tween.add(me); 180 return me; 181 }, 182 183 /** 184 * 连接下一个Tween变换。其开始时间根据delay值不同而不同。当delay值为字符串且以'+'或'-'开始时,Tween的开始时间从当前变换结束点计算,否则以当前变换起始点计算。 185 * @param {Tween} tween 要连接的Tween变换。 186 * @returns {Tween} Tween变换本身。可用于链式调用。 187 */ 188 link: function(tween){ 189 var me = this, delay = tween.delay, startTime = me._startTime; 190 191 if(typeof delay === 'string'){ 192 var plus = delay.indexOf('+') == 0, minus = delay.indexOf('-') == 0; 193 delay = plus || minus ? Number(delay.substr(1)) * (plus ? 1 : -1) : Number(delay); 194 } 195 tween.delay = delay; 196 tween._startTime = plus || minus ? startTime + me.duration + delay : startTime + delay; 197 198 me._next = tween; 199 Tween.remove(tween); 200 return me; 201 }, 202 203 /** 204 * Tween类的内部渲染方法。 205 * @private 206 */ 207 _render: function(ratio){ 208 var me = this, target = me.target, fromProps = me._fromProps, p; 209 for(p in fromProps) target[p] = fromProps[p] + (me._toProps[p] - fromProps[p]) * ratio; 210 }, 211 212 /** 213 * Tween类的内部更新方法。 214 * @private 215 */ 216 _update: function(time, forceUpdate){ 217 var me = this; 218 if(me.paused && !forceUpdate) return; 219 220 //elapsed time 221 var elapsed = time - me._startTime - me._pausedTime + me._seekTime; 222 if(elapsed < 0) return; 223 224 //elapsed ratio 225 var ratio = elapsed / me.duration, complete = false, callback; 226 ratio = ratio <= 0 ? 0 : ratio >= 1 ? 1 : me.ease ? me.ease(ratio) : ratio; 227 228 if(me.reverse){ 229 //backward 230 if(me._reverseFlag < 0) ratio = 1 - ratio; 231 //forward 232 if(ratio < 1e-7){ 233 //repeat complete or not loop 234 if((me.repeat > 0 && me._repeatCount++ >= me.repeat) || (me.repeat == 0 && !me.loop)){ 235 complete = true; 236 }else{ 237 me._startTime = now(); 238 me._pausedTime = 0; 239 me._reverseFlag *= -1; 240 } 241 } 242 } 243 244 //start callback 245 if(me.time == 0 && (callback = me.onStart)) callback.call(me, me); 246 me.time = elapsed; 247 248 //render & update callback 249 me._render(ratio); 250 (callback = me.onUpdate) && callback.call(me, ratio, me); 251 252 //check if complete 253 if(ratio >= 1){ 254 if(me.reverse){ 255 me._startTime = now(); 256 me._pausedTime = 0; 257 me._reverseFlag *= -1; 258 }else if(me.loop || me.repeat > 0 && me._repeatCount++ < me.repeat){ 259 me._startTime = now() + me.repeatDelay; 260 me._pausedTime = 0; 261 }else{ 262 complete = true; 263 } 264 } 265 266 //next tween 267 var next = me._next; 268 if(next && next.time <= 0){ 269 var nextStartTime = next._startTime; 270 if(nextStartTime > 0 && nextStartTime <= time){ 271 //parallel tween 272 next._render(ratio); 273 next.time = elapsed; 274 Tween.add(next); 275 }else if(complete && (nextStartTime < 0 || nextStartTime > time)){ 276 //next tween 277 next.start(); 278 } 279 } 280 281 //complete 282 if(complete){ 283 (callback = me.onComplete) && callback.call(me, me); 284 return true; 285 } 286 }, 287 288 Statics: /** @lends Tween */ { 289 /** 290 * @private 291 */ 292 _tweens: [], 293 294 /** 295 * 更新所有Tween实例。 296 * @returns {Object} Tween。 297 */ 298 tick: function(){ 299 var tweens = Tween._tweens, tween, i, len = tweens.length; 300 301 for(i = 0; i < len; i++){ 302 tween = tweens[i]; 303 if(tween && tween._update(now())){ 304 tweens.splice(i, 1); 305 i--; 306 } 307 } 308 return Tween; 309 }, 310 311 /** 312 * 添加Tween实例。 313 * @param {Tween} tween 要添加的Tween对象。 314 * @returns {Object} Tween。 315 */ 316 add: function(tween){ 317 var tweens = Tween._tweens; 318 if(tweens.indexOf(tween) == -1) tweens.push(tween); 319 return Tween; 320 }, 321 322 /** 323 * 删除Tween实例。 324 * @param {Tween|Object|Array} tweenOrTarget 要删除的Tween对象或target对象或要删除的一组对象。 325 * @returns {Object} Tween。 326 */ 327 remove: function(tweenOrTarget){ 328 if(tweenOrTarget instanceof Array){ 329 for(var i = 0, l = tweenOrTarget.length;i < l;i ++){ 330 Tween.remove(tweenOrTarget[i]); 331 } 332 return Tween; 333 } 334 335 var tweens = Tween._tweens, i; 336 if(tweenOrTarget instanceof Tween){ 337 i = tweens.indexOf(tweenOrTarget); 338 if(i > -1) tweens.splice(i, 1); 339 }else{ 340 for(i = 0; i < tweens.length; i++){ 341 if(tweens[i].target === tweenOrTarget){ 342 tweens.splice(i, 1); 343 i--; 344 } 345 } 346 } 347 348 return Tween; 349 }, 350 351 /** 352 * 删除所有Tween实例。 353 * @returns {Object} Tween。 354 */ 355 removeAll: function(){ 356 Tween._tweens.length = 0; 357 return Tween; 358 }, 359 360 /** 361 * 创建一个缓动动画,让目标对象从开始属性变换到目标属性。 362 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 363 * @param fromProps 缓动目标对象的开始属性。 364 * @param toProps 缓动目标对象的目标属性。 365 * @param params 缓动动画的参数。 366 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 367 */ 368 fromTo: function(target, fromProps, toProps, params){ 369 var isArray = target instanceof Array; 370 target = isArray ? target : [target]; 371 372 var tween, i, stagger = params.stagger, tweens = []; 373 for(i = 0; i < target.length; i++){ 374 tween = new Tween(target[i], fromProps, toProps, params); 375 if(stagger) tween.delay = (params.delay || 0) + (i * stagger || 0); 376 tween.start(); 377 tweens.push(tween); 378 } 379 380 return isArray?tweens:tween; 381 }, 382 383 /** 384 * 创建一个缓动动画,让目标对象从当前属性变换到目标属性。 385 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 386 * @param toProps 缓动目标对象的目标属性。 387 * @param params 缓动动画的参数。 388 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 389 */ 390 to: function(target, toProps, params){ 391 return Tween.fromTo(target, null, toProps, params); 392 }, 393 394 /** 395 * 创建一个缓动动画,让目标对象从指定的起始属性变换到当前属性。 396 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 397 * @param fromProps 缓动目标对象的目标属性。 398 * @param params 缓动动画的参数。 399 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 400 */ 401 from: function(target, fromProps, params){ 402 return Tween.fromTo(target, fromProps, null, params); 403 } 404 } 405 406 }); 407 408 })();