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