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._fromProps = fromProps; 76 me._toProps = toProps; 77 78 //for old version compatiblity 79 if(!params.duration && params.time){ 80 me.duration = params.time || 0; 81 me.time = 0; 82 } 83 }, 84 85 target: null, 86 duration: 1000, 87 delay: 0, 88 paused: false, 89 loop: false, 90 reverse: false, 91 repeat: 0, 92 repeatDelay: 0, 93 ease: null, 94 time: 0, //ready only 95 96 isStart:false, 97 isComplete:false, 98 onStart: null, 99 onUpdate: null, 100 onComplete: null, 101 102 /** 103 * 设置缓动对象的初始和目标属性。 104 * @param {Object} fromProps 缓动对象的初始属性。 105 * @param {Object} toProps 缓动对象的目标属性。 106 * @returns {Tween} Tween变换本身。可用于链式调用。 107 */ 108 setProps: function(fromProps, toProps){ 109 var me = this, target = me.target, 110 propNames = fromProps || toProps, 111 from = me._fromProps = {}, to = me._toProps = {}; 112 113 fromProps = fromProps || target; 114 toProps = toProps || target; 115 116 for(var p in propNames){ 117 to[p] = toProps[p] || 0; 118 target[p] = from[p] = fromProps[p] || 0; 119 } 120 return me; 121 }, 122 123 /** 124 * 启动缓动动画的播放。 125 * @returns {Tween} Tween变换本身。可用于链式调用。 126 */ 127 start: function(){ 128 var me = this; 129 me._startTime = now() + me.delay; 130 me._seekTime = 0; 131 me._pausedTime = 0; 132 me._reverseFlag = 1; 133 me._repeatCount = 0; 134 me.paused = false; 135 me.isStart = false; 136 me.isComplete = false; 137 Tween.add(me); 138 return me; 139 }, 140 141 /** 142 * 停止缓动动画的播放。 143 * @returns {Tween} Tween变换本身。可用于链式调用。 144 */ 145 stop: function(){ 146 Tween.remove(this); 147 return this; 148 }, 149 150 /** 151 * 暂停缓动动画的播放。 152 * @returns {Tween} Tween变换本身。可用于链式调用。 153 */ 154 pause: function(){ 155 var me = this; 156 me.paused = true; 157 me._pausedStartTime = now(); 158 return me; 159 }, 160 161 /** 162 * 恢复缓动动画的播放。 163 * @returns {Tween} Tween变换本身。可用于链式调用。 164 */ 165 resume: function(){ 166 var me = this; 167 me.paused = false; 168 if(me._pausedStartTime) me._pausedTime += now() - me._pausedStartTime; 169 me._pausedStartTime = 0; 170 return me; 171 }, 172 173 /** 174 * 跳转Tween到指定的时间。 175 * @param {Number} time 指定要跳转的时间。取值范围为:0 - duraion。 176 * @param {Boolean} pause 是否暂停。 177 * @returns {Tween} Tween变换本身。可用于链式调用。 178 */ 179 seek: function(time, pause){ 180 var me = this, current = now(); 181 me._startTime = current; 182 me._seekTime = time; 183 me._pausedTime = 0; 184 if(pause !== undefined) me.paused = pause; 185 me._update(current, true); 186 Tween.add(me); 187 return me; 188 }, 189 190 /** 191 * 连接下一个Tween变换。其开始时间根据delay值不同而不同。当delay值为字符串且以'+'或'-'开始时,Tween的开始时间从当前变换结束点计算,否则以当前变换起始点计算。 192 * @param {Tween} tween 要连接的Tween变换。 193 * @returns {Tween} 下一个Tween。可用于链式调用。 194 */ 195 link: function(tween){ 196 var me = this, delay = tween.delay, startTime = me._startTime; 197 198 var plus, minus; 199 if(typeof delay === 'string'){ 200 plus = delay.indexOf('+') == 0; 201 minus = delay.indexOf('-') == 0; 202 delay = plus || minus ? Number(delay.substr(1)) * (plus ? 1 : -1) : Number(delay); 203 } 204 tween.delay = delay; 205 tween._startTime = plus || minus ? startTime + me.duration + delay : startTime + delay; 206 207 me._next = tween; 208 Tween.remove(tween); 209 return tween; 210 }, 211 212 /** 213 * Tween类的内部渲染方法。 214 * @private 215 */ 216 _render: function(ratio){ 217 var me = this, target = me.target, fromProps = me._fromProps, p; 218 for(p in fromProps) target[p] = fromProps[p] + (me._toProps[p] - fromProps[p]) * ratio; 219 }, 220 221 /** 222 * Tween类的内部更新方法。 223 * @private 224 */ 225 _update: function(time, forceUpdate){ 226 var me = this; 227 if(me.paused && !forceUpdate) return; 228 if(me.isComplete) return true; 229 230 //elapsed time 231 var elapsed = time - me._startTime - me._pausedTime + me._seekTime; 232 if(elapsed < 0) return; 233 234 //elapsed ratio 235 var ratio = elapsed / me.duration, complete = false, callback; 236 ratio = ratio <= 0 ? 0 : ratio >= 1 ? 1 : ratio; 237 var easeRatio = me.ease ? me.ease(ratio) : ratio; 238 239 if(me.reverse && me.isStart){ 240 //backward 241 if(me._reverseFlag < 0) { 242 ratio = 1 - ratio; 243 easeRatio = 1 - easeRatio; 244 } 245 //forward 246 if(ratio < 1e-7){ 247 //repeat complete or not loop 248 if((me.repeat > 0 && me._repeatCount++ >= me.repeat) || (me.repeat == 0 && !me.loop)){ 249 complete = true; 250 }else{ 251 me._startTime = now(); 252 me._pausedTime = 0; 253 me._reverseFlag *= -1; 254 } 255 } 256 } 257 258 //start callback 259 if(!me.isStart) { 260 me.setProps(me._fromProps, me._toProps); 261 me.isStart = true; 262 if(me.onStart){ 263 me.onStart.call(me, me); 264 } 265 } 266 me.time = elapsed; 267 268 //render & update callback 269 me._render(easeRatio); 270 (callback = me.onUpdate) && callback.call(me, ratio, me); 271 272 //check if complete 273 if(ratio >= 1){ 274 if(me.reverse){ 275 me._startTime = now(); 276 me._pausedTime = 0; 277 me._reverseFlag *= -1; 278 }else if(me.loop || me.repeat > 0 && me._repeatCount++ < me.repeat){ 279 me._startTime = now() + me.repeatDelay; 280 me._pausedTime = 0; 281 }else{ 282 me.isComplete = true; 283 } 284 } 285 286 //next tween 287 var next = me._next; 288 if(next && next.time <= 0){ 289 var nextStartTime = next._startTime; 290 if(nextStartTime > 0 && nextStartTime <= time){ 291 //parallel tween 292 next._render(ratio); 293 next.time = elapsed; 294 Tween.add(next); 295 }else if(me.isComplete && (nextStartTime < 0 || nextStartTime > time)){ 296 //next tween 297 next.start(); 298 } 299 } 300 301 //complete 302 if(me.isComplete){ 303 (callback = me.onComplete) && callback.call(me, me); 304 return true; 305 } 306 }, 307 308 Statics: /** @lends Tween */ { 309 /** 310 * @private 311 */ 312 _tweens: [], 313 314 /** 315 * 更新所有Tween实例。 316 * @returns {Object} Tween。 317 */ 318 tick: function(){ 319 var tweens = Tween._tweens, tween, i, len = tweens.length; 320 321 for(i = 0; i < len; i++){ 322 tween = tweens[i]; 323 if(tween && tween._update(now())){ 324 tweens.splice(i, 1); 325 i--; 326 } 327 } 328 return Tween; 329 }, 330 331 /** 332 * 添加Tween实例。 333 * @param {Tween} tween 要添加的Tween对象。 334 * @returns {Object} Tween。 335 */ 336 add: function(tween){ 337 var tweens = Tween._tweens; 338 if(tweens.indexOf(tween) == -1) tweens.push(tween); 339 return Tween; 340 }, 341 342 /** 343 * 删除Tween实例。 344 * @param {Tween|Object|Array} tweenOrTarget 要删除的Tween对象或target对象或要删除的一组对象。 345 * @returns {Object} Tween。 346 */ 347 remove: function(tweenOrTarget){ 348 var i, l; 349 if(tweenOrTarget instanceof Array){ 350 for(i = 0, l = tweenOrTarget.length;i < l;i ++){ 351 Tween.remove(tweenOrTarget[i]); 352 } 353 return Tween; 354 } 355 356 var tweens = Tween._tweens; 357 if(tweenOrTarget instanceof Tween){ 358 i = tweens.indexOf(tweenOrTarget); 359 if(i > -1) tweens.splice(i, 1); 360 }else{ 361 for(i = 0; i < tweens.length; i++){ 362 if(tweens[i].target === tweenOrTarget){ 363 tweens.splice(i, 1); 364 i--; 365 } 366 } 367 } 368 369 return Tween; 370 }, 371 372 /** 373 * 删除所有Tween实例。 374 * @returns {Object} Tween。 375 */ 376 removeAll: function(){ 377 Tween._tweens.length = 0; 378 return Tween; 379 }, 380 381 /** 382 * 创建一个缓动动画,让目标对象从开始属性变换到目标属性。 383 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 384 * @param fromProps 缓动目标对象的开始属性。 385 * @param toProps 缓动目标对象的目标属性。 386 * @param params 缓动动画的参数。 387 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 388 */ 389 fromTo: function(target, fromProps, toProps, params){ 390 params = params || {}; 391 var isArray = target instanceof Array; 392 target = isArray ? target : [target]; 393 394 var tween, i, stagger = params.stagger, tweens = []; 395 for(i = 0; i < target.length; i++){ 396 tween = new Tween(target[i], fromProps, toProps, params); 397 if(stagger) tween.delay = (params.delay || 0) + (i * stagger || 0); 398 tween.start(); 399 tweens.push(tween); 400 } 401 402 return isArray?tweens:tween; 403 }, 404 405 /** 406 * 创建一个缓动动画,让目标对象从当前属性变换到目标属性。 407 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 408 * @param toProps 缓动目标对象的目标属性。 409 * @param params 缓动动画的参数。 410 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 411 */ 412 to: function(target, toProps, params){ 413 return Tween.fromTo(target, null, toProps, params); 414 }, 415 416 /** 417 * 创建一个缓动动画,让目标对象从指定的起始属性变换到当前属性。 418 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 419 * @param fromProps 缓动目标对象的初始属性。 420 * @param params 缓动动画的参数。 421 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 422 */ 423 from: function(target, fromProps, params){ 424 return Tween.fromTo(target, fromProps, null, params); 425 } 426 } 427 428 }); 429 430 })(); 431