/** * Hilo * Copyright 2015 alibaba.com * Licensed under the MIT License */ /** * *
* 使用示例: *
 * ticker.addTick(Hilo.Tween);//需要把Tween加到ticker里才能使用
 *
 * var view = new View({x:5, y:10});
 * Hilo.Tween.to(view, {
 *     x:100,
 *     y:20,
 *     alpha:0
 * }, {
 *     duration:1000,
 *     delay:500,
 *     ease:Hilo.Ease.Quad.EaseIn,
 *     onComplete:function(){
 *         console.log('complete');
 *     }
 * });
 * 
* @class Tween类提供缓动功能。 * @param {Object} target 缓动对象。 * @param {Object} fromProps 对象缓动的起始属性集合。 * @param {Object} toProps 对象缓动的目标属性集合。 * @param {Object} params 缓动参数。可包含Tween类所有可写属性。 * @module hilo/tween/Tween * @requires hilo/core/Class * @property {Object} target 缓动目标。只读属性。 * @property {Int} duration 缓动总时长。单位毫秒。 * @property {Int} delay 缓动延迟时间。单位毫秒。 * @property {Boolean} paused 缓动是否暂停。默认为false。 * @property {Boolean} loop 缓动是否循环。默认为false。 * @property {Boolean} reverse 缓动是否反转播放。默认为false。 * @property {Int} repeat 缓动重复的次数。默认为0。 * @property {Int} repeatDelay 缓动重复的延迟时长。单位为毫秒。 * @property {Function} ease 缓动变化函数。默认为null。 * @property {Int} time 缓动已进行的时长。单位毫秒。只读属性。 * @property {Function} onStart 缓动开始回调函数。它接受1个参数:tween。默认值为null。 * @property {Function} onUpdate 缓动更新回调函数。它接受2个参数:ratio和tween。默认值为null。 * @property {Function} onComplete 缓动结束回调函数。它接受1个参数:tween。默认值为null。 */ var Tween = (function(){ function now(){ return +new Date(); } return Class.create(/** @lends Tween.prototype */{ constructor: function(target, fromProps, toProps, params){ var me = this; me.target = target; me._startTime = 0; me._seekTime = 0; me._pausedTime = 0; me._pausedStartTime = 0; me._reverseFlag = 1; me._repeatCount = 0; //no fromProps if pass 3 arguments if(arguments.length == 3){ params = toProps; toProps = fromProps; fromProps = null; } for(var p in params) me[p] = params[p]; me._fromProps = fromProps; me._toProps = toProps; //for old version compatiblity if(!params.duration && params.time){ me.duration = params.time || 0; me.time = 0; } }, target: null, duration: 1000, delay: 0, paused: false, loop: false, reverse: false, repeat: 0, repeatDelay: 0, ease: null, time: 0, //ready only isStart:false, isComplete:false, onStart: null, onUpdate: null, onComplete: null, /** * 设置缓动对象的初始和目标属性。 * @param {Object} fromProps 缓动对象的初始属性。 * @param {Object} toProps 缓动对象的目标属性。 * @returns {Tween} Tween变换本身。可用于链式调用。 */ setProps: function(fromProps, toProps){ var me = this, target = me.target, propNames = fromProps || toProps, from = me._fromProps = {}, to = me._toProps = {}; fromProps = fromProps || target; toProps = toProps || target; for(var p in propNames){ to[p] = toProps[p] || 0; target[p] = from[p] = fromProps[p] || 0; } return me; }, /** * 启动缓动动画的播放。 * @returns {Tween} Tween变换本身。可用于链式调用。 */ start: function(){ var me = this; me._startTime = now() + me.delay; me._seekTime = 0; me._pausedTime = 0; me._reverseFlag = 1; me._repeatCount = 0; me.paused = false; me.isStart = false; me.isComplete = false; Tween.add(me); return me; }, /** * 停止缓动动画的播放。 * @returns {Tween} Tween变换本身。可用于链式调用。 */ stop: function(){ Tween.remove(this); return this; }, /** * 暂停缓动动画的播放。 * @returns {Tween} Tween变换本身。可用于链式调用。 */ pause: function(){ var me = this; me.paused = true; me._pausedStartTime = now(); return me; }, /** * 恢复缓动动画的播放。 * @returns {Tween} Tween变换本身。可用于链式调用。 */ resume: function(){ var me = this; me.paused = false; if(me._pausedStartTime) me._pausedTime += now() - me._pausedStartTime; me._pausedStartTime = 0; return me; }, /** * 跳转Tween到指定的时间。 * @param {Number} time 指定要跳转的时间。取值范围为:0 - duraion。 * @param {Boolean} pause 是否暂停。 * @returns {Tween} Tween变换本身。可用于链式调用。 */ seek: function(time, pause){ var me = this, current = now(); me._startTime = current; me._seekTime = time; me._pausedTime = 0; if(pause !== undefined) me.paused = pause; me._update(current, true); Tween.add(me); return me; }, /** * 连接下一个Tween变换。其开始时间根据delay值不同而不同。当delay值为字符串且以'+'或'-'开始时,Tween的开始时间从当前变换结束点计算,否则以当前变换起始点计算。 * @param {Tween} tween 要连接的Tween变换。 * @returns {Tween} 下一个Tween。可用于链式调用。 */ link: function(tween){ var me = this, delay = tween.delay, startTime = me._startTime; var plus, minus; if(typeof delay === 'string'){ plus = delay.indexOf('+') == 0; minus = delay.indexOf('-') == 0; delay = plus || minus ? Number(delay.substr(1)) * (plus ? 1 : -1) : Number(delay); } tween.delay = delay; tween._startTime = plus || minus ? startTime + me.duration + delay : startTime + delay; me._next = tween; Tween.remove(tween); return tween; }, /** * Tween类的内部渲染方法。 * @private */ _render: function(ratio){ var me = this, target = me.target, fromProps = me._fromProps, p; for(p in fromProps) target[p] = fromProps[p] + (me._toProps[p] - fromProps[p]) * ratio; }, /** * Tween类的内部更新方法。 * @private */ _update: function(time, forceUpdate){ var me = this; if(me.paused && !forceUpdate) return; if(me.isComplete) return true; //elapsed time var elapsed = time - me._startTime - me._pausedTime + me._seekTime; if(elapsed < 0) return; //elapsed ratio var ratio = elapsed / me.duration, complete = false, callback; ratio = ratio <= 0 ? 0 : ratio >= 1 ? 1 : ratio; var easeRatio = me.ease ? me.ease(ratio) : ratio; if(me.reverse && me.isStart){ //backward if(me._reverseFlag < 0) { ratio = 1 - ratio; easeRatio = 1 - easeRatio; } //forward if(ratio < 1e-7){ //repeat complete or not loop if((me.repeat > 0 && me._repeatCount++ >= me.repeat) || (me.repeat == 0 && !me.loop)){ complete = true; }else{ me._startTime = now(); me._pausedTime = 0; me._reverseFlag *= -1; } } } //start callback if(!me.isStart) { me.setProps(me._fromProps, me._toProps); me.isStart = true; if(me.onStart){ me.onStart.call(me, me); } } me.time = elapsed; //render & update callback me._render(easeRatio); (callback = me.onUpdate) && callback.call(me, ratio, me); //check if complete if(ratio >= 1){ if(me.reverse){ me._startTime = now(); me._pausedTime = 0; me._reverseFlag *= -1; }else if(me.loop || me.repeat > 0 && me._repeatCount++ < me.repeat){ me._startTime = now() + me.repeatDelay; me._pausedTime = 0; }else{ me.isComplete = true; } } //next tween var next = me._next; if(next && next.time <= 0){ var nextStartTime = next._startTime; if(nextStartTime > 0 && nextStartTime <= time){ //parallel tween next._render(ratio); next.time = elapsed; Tween.add(next); }else if(me.isComplete && (nextStartTime < 0 || nextStartTime > time)){ //next tween next.start(); } } //complete if(me.isComplete){ (callback = me.onComplete) && callback.call(me, me); return true; } }, Statics: /** @lends Tween */ { /** * @private */ _tweens: [], /** * 更新所有Tween实例。 * @returns {Object} Tween。 */ tick: function(){ var tweens = Tween._tweens, tween, i, len = tweens.length; for(i = 0; i < len; i++){ tween = tweens[i]; if(tween && tween._update(now())){ tweens.splice(i, 1); i--; } } return Tween; }, /** * 添加Tween实例。 * @param {Tween} tween 要添加的Tween对象。 * @returns {Object} Tween。 */ add: function(tween){ var tweens = Tween._tweens; if(tweens.indexOf(tween) == -1) tweens.push(tween); return Tween; }, /** * 删除Tween实例。 * @param {Tween|Object|Array} tweenOrTarget 要删除的Tween对象或target对象或要删除的一组对象。 * @returns {Object} Tween。 */ remove: function(tweenOrTarget){ var i, l; if(tweenOrTarget instanceof Array){ for(i = 0, l = tweenOrTarget.length;i < l;i ++){ Tween.remove(tweenOrTarget[i]); } return Tween; } var tweens = Tween._tweens; if(tweenOrTarget instanceof Tween){ i = tweens.indexOf(tweenOrTarget); if(i > -1) tweens.splice(i, 1); }else{ for(i = 0; i < tweens.length; i++){ if(tweens[i].target === tweenOrTarget){ tweens.splice(i, 1); i--; } } } return Tween; }, /** * 删除所有Tween实例。 * @returns {Object} Tween。 */ removeAll: function(){ Tween._tweens.length = 0; return Tween; }, /** * 创建一个缓动动画,让目标对象从开始属性变换到目标属性。 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 * @param fromProps 缓动目标对象的开始属性。 * @param toProps 缓动目标对象的目标属性。 * @param params 缓动动画的参数。 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 */ fromTo: function(target, fromProps, toProps, params){ params = params || {}; var isArray = target instanceof Array; target = isArray ? target : [target]; var tween, i, stagger = params.stagger, tweens = []; for(i = 0; i < target.length; i++){ tween = new Tween(target[i], fromProps, toProps, params); if(stagger) tween.delay = (params.delay || 0) + (i * stagger || 0); tween.start(); tweens.push(tween); } return isArray?tweens:tween; }, /** * 创建一个缓动动画,让目标对象从当前属性变换到目标属性。 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 * @param toProps 缓动目标对象的目标属性。 * @param params 缓动动画的参数。 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 */ to: function(target, toProps, params){ return Tween.fromTo(target, null, toProps, params); }, /** * 创建一个缓动动画,让目标对象从指定的起始属性变换到当前属性。 * @param {Object|Array} target 缓动目标对象或缓动目标数组。 * @param fromProps 缓动目标对象的初始属性。 * @param params 缓动动画的参数。 * @returns {Tween|Array} 一个Tween实例对象或Tween实例数组。 */ from: function(target, fromProps, params){ return Tween.fromTo(target, fromProps, null, params); } } }); })();