2019-04-17 15:53:46 +08:00

431 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Hilo
* Copyright 2015 alibaba.com
* Licensed under the MIT License
*/
/**
* <iframe src='../../../examples/Tween.html?noHeader' width = '550' height = '130' scrolling='no'></iframe>
* <br/>
* 使用示例:
* <pre>
* 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');
* }
* });
* </pre>
* @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);
}
}
});
})();