说好的缓动呢?
jQuery的缓动函数不给力,被逼自己写一个,既然要写,先把需求列好:
1. 动画类型有:linear, easeIn, easeOut, easeInOut
2. 可以在缓动过程中改变属性,也可以是调用函数,并改变函数的参数(后者是我的初衷,jq只支持属性,见 当0碰上0 )
动画是怎样产生的?学过Flash的人应该知道帧,帧就是一个画面,通常一秒25帧,也就一秒播放25个画面,如此快速的放过去,静态也成了动态(当然,前提是25个画面不是同一个画面,囧。。。)
难道做动画要画N多图,好吧,这叫“逐帧动画”,如果你很闲大可以这么干,因为这样做的效果绝对是顶级的,毫无瑕疵的。但是我一点都不闲,我画画的水平连幼儿园都教不了,所以这条路可以直接枪毙。
接下来进入正题,Flash中还有关键帧的概念,举个例子,关键帧A,物体box的宽高为100,关键帧B,box的宽高为50,A与B之间有25帧,接着我们从A播放到B,效果就是box在1秒内不断缩小,这叫“补间动画”。
这里涉及到三个东西:动画对象(box),动画属性(width, height),动画时间(1秒)
我们要做的事就是在某个时间点确定动画属性的值
常见的动画有四种类型,介绍一下:
linear:线性动画,即匀速
easeIn:速度从小到大,即淡入
easeOut :速度从大到小,即淡出
easeInOut:开始时速度从小到大,结束时速度从大到小,即淡入淡出
====================下面这段爱看不看,估计看着挺无聊的===========================
其实说到缓动,就不得不提Robert Penner,他发明了N多缓动公式,举个例子
/**
* @param t 用掉的时间
* @param b 初始值
* @param c 总变化量,比如x的初始值为0,结束值为100,变化量就是100
* @param d 动画总时长
* @return 当前属性值
*/
function linear(t, b, c, d){
return c * t / d + b;
}
我还是解释一下吧:
设当前变化量为X,则
t / d = X / c,所以X = c * t / d,然后X + b就可以获得当前属性值
再看一个稍复杂的:
var easeInQuad = function(t, b, c, d){
return c * ( t /= d ) * t + b;
}
这个有淡入效果,也就是说动画开始时,值的变化量从小到大。
可以发现两者唯一的区别就是 t / d 和 (t /= d) * t,刚才说了t / d是一个>=0 && <=1的比值,暂取名为a,而(t /= d) * t就相当于Math.pow(a, 2)。
为什么要平方呢?
1. 首先a >= Math.pow(a, 2)是肯定的
2. 每次调用函数时,t / d 这个比值也是匀速变大的,比如第1次调用时是0.1(平方0.01),第2次调用时是0.2(平方0.04)等,那第10次调用时,肯定是1没错吧,这时候 c * 1 + b,动画就到此结束了
3. 第2点证明了比值越小,值的变化量就越小,比值越大,值的变化量就越大,如果不用平方而是三次方,那淡入效果就更明显了。
举个两个例子,可以看出一些规律,公式大体便是 当前属性值 = 总变化量 * 增量系数 + 初始值, 其中,增量系数根据不同算法各有不同。
====================继续往下看吧===========================
增量系数的算法,我就从 司徒正美 那拿过来用吧,他总结了一大堆,必须收藏啊!!
var tween = {
linear : function(pos) {
return 1;
},
easeInQuad : function(pos) {
return Math.pow(pos, 2);
},
easeOutQuad : function(pos) {
return -(Math.pow(( pos - 1), 2) - 1);
},
easeInOutQuad : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 2);
return -0.5 * ((pos -= 2) * pos - 2);
},
easeInCubic : function(pos) {
return Math.pow(pos, 3);
},
easeOutCubic : function(pos) {
return (Math.pow(( pos - 1), 3) + 1);
},
easeInOutCubic : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 3);
return 0.5 * (Math.pow(( pos - 2), 3) + 2);
},
easeInQuart : function(pos) {
return Math.pow(pos, 4);
},
easeOutQuart : function(pos) {
return -(Math.pow(( pos - 1), 4) - 1)
},
easeInOutQuart : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 4);
return -0.5 * ((pos -= 2) * Math.pow(pos, 3) - 2);
},
easeInQuint : function(pos) {
return Math.pow(pos, 5);
},
easeOutQuint : function(pos) {
return (Math.pow(( pos - 1), 5) + 1);
},
easeInOutQuint : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 5);
return 0.5 * (Math.pow(( pos - 2), 5) + 2);
},
easeInSine : function(pos) {
return -Math.cos(pos * (Math.PI / 2)) + 1;
},
easeOutSine : function(pos) {
return Math.sin(pos * (Math.PI / 2));
},
easeInOutSine : function(pos) {
return (-.5 * (Math.cos(Math.PI * pos) - 1));
},
easeInExpo : function(pos) {
return (pos == 0) ? 0 : Math.pow(2, 10 * ( pos - 1));
},
easeOutExpo : function(pos) {
return (pos == 1) ? 1 : -Math.pow(2, -10 * pos) + 1;
},
easeInOutExpo : function(pos) {
if(pos == 0)
return 0;
if(pos == 1)
return 1;
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(2, 10 * ( pos - 1));
return 0.5 * (-Math.pow(2, -10 * --pos) + 2);
},
easeInCirc : function(pos) {
return -(Math.sqrt(1 - (pos * pos)) - 1);
},
easeOutCirc : function(pos) {
return Math.sqrt(1 - Math.pow(( pos - 1), 2))
},
easeInOutCirc : function(pos) {
if((pos /= 0.5) < 1)
return -0.5 * (Math.sqrt(1 - pos * pos) - 1);
return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1);
},
easeOutBounce : function(pos) {
if((pos) < (1 / 2.75)) {
return (7.5625 * pos * pos);
} else if(pos < (2 / 2.75)) {
return (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
} else if(pos < (2.5 / 2.75)) {
return (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
} else {
return (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
}
},
easeInBack : function(pos) {
var s = 1.70158;
return (pos) * pos * ((s + 1) * pos - s);
},
easeOutBack : function(pos) {
var s = 1.70158;
return ( pos = pos - 1) * pos * ((s + 1) * pos + s) + 1;
},
easeInOutBack : function(pos) {
var s = 1.70158;
if((pos /= 0.5) < 1)
return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s));
return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2);
},
elastic : function(pos) {
return -1 * Math.pow(4, -8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1;
},
swingFromTo : function(pos) {
var s = 1.70158;
return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2);
},
swingFrom : function(pos) {
var s = 1.70158;
return pos * pos * ((s + 1) * pos - s);
},
swingTo : function(pos) {
var s = 1.70158;
return (pos -= 1) * pos * ((s + 1) * pos + s) + 1;
},
bounce : function(pos) {
if(pos < (1 / 2.75)) {
return (7.5625 * pos * pos);
} else if(pos < (2 / 2.75)) {
return (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
} else if(pos < (2.5 / 2.75)) {
return (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
} else {
return (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
}
},
bouncePast : function(pos) {
if(pos < (1 / 2.75)) {
return (7.5625 * pos * pos);
} else if(pos < (2 / 2.75)) {
return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
} else if(pos < (2.5 / 2.75)) {
return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
} else {
return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
}
},
easeFromTo : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 4);
return -0.5 * ((pos -= 2) * Math.pow(pos, 3) - 2);
},
easeFrom : function(pos) {
return Math.pow(pos, 4);
},
easeTo : function(pos) {
return Math.pow(pos, 0.25);
},
linear : function(pos) {
return pos
},
sinusoidal : function(pos) {
return (-Math.cos(pos * Math.PI) / 2) + 0.5;
},
reverse : function(pos) {
return 1 - pos;
},
mirror : function(pos, transition) {
transition = transition || tween.sinusoidal;
if(pos < 0.5)
return transition(pos * 2);
else
return transition(1 - ( pos - 0.5) * 2);
},
flicker : function(pos) {
var pos = pos + (Math.random() - 0.5) / 5;
return tween.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos);
},
wobble : function(pos) {
return (-Math.cos(pos * Math.PI * (9 * pos)) / 2) + 0.5;
},
pulse : function(pos, pulses) {
return (-Math.cos((pos * ((pulses || 5) - .5) * 2) * Math.PI) / 2) + .5;
},
blink : function(pos, blinks) {
return Math.round(pos * (blinks || 5)) % 2;
},
spring : function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
none : function(pos) {
return 0;
}
};
你会发现有Quad,Cubic,Quart之类的字眼,这些到底是啥意思呢,这里我给出Flash的Tweener 官方文档,一看便知了(如果下面是空白,表示在缓冲,为了看到这么好的演示,耐心等会吧)
根据上述知识点,我的动画函数便出炉了
/**
* @param {HTMLElement/Function} obj 属性时:动画对象; 方法时: 方法引用
* @param {Object} params 属性时:{属性: 结束值}; 方法时:{startArgs: 初始参数[数组], endArgs: 结束参数[数组]};
* @param {Number} duration 动画总时长,单位为毫秒
* @param {String} type 动画类型,默认为linear,具体值参考tween下的属性名
* @param {Function} callback 动画结束时执行的回调函数
*/
var animate = function(obj, params, duration, type, callback){
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var tween = {
linear : function(pos) {
return 1;
},
easeInQuad : function(pos) {
return Math.pow(pos, 2);
},
easeOutQuad : function(pos) {
return -(Math.pow(( pos - 1), 2) - 1);
},
easeInOutQuad : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 2);
return -0.5 * ((pos -= 2) * pos - 2);
},
easeInCubic : function(pos) {
return Math.pow(pos, 3);
},
easeOutCubic : function(pos) {
return (Math.pow(( pos - 1), 3) + 1);
},
easeInOutCubic : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 3);
return 0.5 * (Math.pow(( pos - 2), 3) + 2);
},
easeInQuart : function(pos) {
return Math.pow(pos, 4);
},
easeOutQuart : function(pos) {
return -(Math.pow(( pos - 1), 4) - 1)
},
easeInOutQuart : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 4);
return -0.5 * ((pos -= 2) * Math.pow(pos, 3) - 2);
},
easeInQuint : function(pos) {
return Math.pow(pos, 5);
},
easeOutQuint : function(pos) {
return (Math.pow(( pos - 1), 5) + 1);
},
easeInOutQuint : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 5);
return 0.5 * (Math.pow(( pos - 2), 5) + 2);
},
easeInSine : function(pos) {
return -Math.cos(pos * (Math.PI / 2)) + 1;
},
easeOutSine : function(pos) {
return Math.sin(pos * (Math.PI / 2));
},
easeInOutSine : function(pos) {
return (-.5 * (Math.cos(Math.PI * pos) - 1));
},
easeInExpo : function(pos) {
return (pos == 0) ? 0 : Math.pow(2, 10 * ( pos - 1));
},
easeOutExpo : function(pos) {
return (pos == 1) ? 1 : -Math.pow(2, -10 * pos) + 1;
},
easeInOutExpo : function(pos) {
if(pos == 0)
return 0;
if(pos == 1)
return 1;
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(2, 10 * ( pos - 1));
return 0.5 * (-Math.pow(2, -10 * --pos) + 2);
},
easeInCirc : function(pos) {
return -(Math.sqrt(1 - (pos * pos)) - 1);
},
easeOutCirc : function(pos) {
return Math.sqrt(1 - Math.pow(( pos - 1), 2))
},
easeInOutCirc : function(pos) {
if((pos /= 0.5) < 1)
return -0.5 * (Math.sqrt(1 - pos * pos) - 1);
return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1);
},
easeOutBounce : function(pos) {
if((pos) < (1 / 2.75)) {
return (7.5625 * pos * pos);
} else if(pos < (2 / 2.75)) {
return (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
} else if(pos < (2.5 / 2.75)) {
return (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
} else {
return (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
}
},
easeInBack : function(pos) {
var s = 1.70158;
return (pos) * pos * ((s + 1) * pos - s);
},
easeOutBack : function(pos) {
var s = 1.70158;
return ( pos = pos - 1) * pos * ((s + 1) * pos + s) + 1;
},
easeInOutBack : function(pos) {
var s = 1.70158;
if((pos /= 0.5) < 1)
return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s));
return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2);
},
elastic : function(pos) {
return -1 * Math.pow(4, -8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1;
},
swingFromTo : function(pos) {
var s = 1.70158;
return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2);
},
swingFrom : function(pos) {
var s = 1.70158;
return pos * pos * ((s + 1) * pos - s);
},
swingTo : function(pos) {
var s = 1.70158;
return (pos -= 1) * pos * ((s + 1) * pos + s) + 1;
},
bounce : function(pos) {
if(pos < (1 / 2.75)) {
return (7.5625 * pos * pos);
} else if(pos < (2 / 2.75)) {
return (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
} else if(pos < (2.5 / 2.75)) {
return (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
} else {
return (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
}
},
bouncePast : function(pos) {
if(pos < (1 / 2.75)) {
return (7.5625 * pos * pos);
} else if(pos < (2 / 2.75)) {
return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75);
} else if(pos < (2.5 / 2.75)) {
return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375);
} else {
return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375);
}
},
easeFromTo : function(pos) {
if((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 4);
return -0.5 * ((pos -= 2) * Math.pow(pos, 3) - 2);
},
easeFrom : function(pos) {
return Math.pow(pos, 4);
},
easeTo : function(pos) {
return Math.pow(pos, 0.25);
},
linear : function(pos) {
return pos
},
sinusoidal : function(pos) {
return (-Math.cos(pos * Math.PI) / 2) + 0.5;
},
reverse : function(pos) {
return 1 - pos;
},
mirror : function(pos, transition) {
transition = transition || tween.sinusoidal;
if(pos < 0.5)
return transition(pos * 2);
else
return transition(1 - ( pos - 0.5) * 2);
},
flicker : function(pos) {
var pos = pos + (Math.random() - 0.5) / 5;
return tween.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos);
},
wobble : function(pos) {
return (-Math.cos(pos * Math.PI * (9 * pos)) / 2) + 0.5;
},
pulse : function(pos, pulses) {
return (-Math.cos((pos * ((pulses || 5) - .5) * 2) * Math.PI) / 2) + .5;
},
blink : function(pos, blinks) {
return Math.round(pos * (blinks || 5)) % 2;
},
spring : function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
none : function(pos) {
return 0;
}
};
animate = function(obj, params, duration, type, callback){
var startValue = {},//初始值
changeValue = null,//总变化量
startTime = new Date().getTime(),
ease = tween[type || 'linear'];
if(typeof obj !== 'function'){
obj = obj.style;
for(var name in params){
startValue[name] = getStyle(obj, name);
}
changeValue = {};
for(name in params){
changeValue[name] = params[name] - startValue[name];
}
}else{
changeValue = [];
var startArgs = params.startArgs,
endArgs = params.endArgs,
toString = Object.prototype.toString;
for(var i = 0, len = startArgs.length; i < len; i++){
changeValue[i] = endArgs[i] - startArgs[i];
}
}
var run = function(){
var timeStamp = new Date().getTime() - startTime,
factor = ease(timeStamp / duration);//增量系数
if(!(toString.call(changeValue) === '[object Array]')){
for(name in params){
obj[name] = (changeValue[name] * factor + startValue[name]) + 'px';//套公式吧,亲
}
}else{
var curArgs = [];
for(i = 0; i < len; i++){
curArgs[i] = changeValue[i] * factor + startArgs[i];
}
obj.apply(null, curArgs);
}
if(factor < 1){
window.requestAnimationFrame(run);
}else{
if(callback)callback();
}
};
window.requestAnimationFrame(run);
}
animate(obj, params, duration, type, callback);
};
var getStyle = function(el, style){
var value = null;
if(window.defaultView){
value = window.getComputedStyle(el, null).getPropertyValue(style);
}else{
style = style.replace(/\-(\w)/g, function($0, $1){
return $1.toUpperCase();
});
value = el.currentStyle[style];
if(value === 'auto'){
value = '0';
}
}
return parseFloat(value) || 0;
};

浙公网安备 33010602011771号