jquery animate函数是作用在css属性上的,但有些时候我们的动画不是作用在css属性之上,还是老问题在3s内,将一个div上显示的数字从0变到100.
在上一篇博客 jquery animate妙用 中我们曾探讨过这个问题,也找到了一个巧妙地方法,现在的话,忘了之前你看到的吧,我们从另一个角度来思考一下这个问题。
jquery的animate已经实现了css属性动画,我们没必要去重复发明轮子,我们需要的是来弥补jquery的不足:实现一个值动画的animate。那什么是值动画呢?通常我们要实现一个动画,思路一般就是将其先分解为多少帧,然后每一帧去改变它的状态,如果我们只是想让一个数据在某一段时间从x变到y,而在这个过程中我们需要使用这个不断变化的值去做一些事,可能不是去更新ui,但也可能事,我们就姑且称之为动画吧,因为动画一般是指ui在一段时间发生连续性的变化,而我们的是数据。举个栗子,正如我之前提到的老问题:3s内,将一个div上显示的数字从0变到100(这个栗子太好了,哪里都能用,皮黄心白,口味甘甜,实在是居家旅行,出门必备品)。那么我们希望的animate是这样的:
animate(0,100,3000,
function
(val){
div.text(a)
})
四个参数一次是初始值 最终值 动画时间 和每一帧的回调(val范围是[0,100]) ,是不是很清晰? 这就打完收工了? no ! 如果我的要求变一下,在3s内,将一个div上显示的数字从0变到100,同时将它的left值从0变到785. 这怎么办? 写上两个animate? 显然不好,也许改成这样你会欢喜:
animate({text:100,left:785},3000,
function
(o){
div.text(o.text).css(
"left"
:o.left)
})
这样好像很棒,支持多个数据变化,看看这是不是似曾相识的感觉?这和jquery的animate太像了,其实jquery的animate是可以实现这样的功能的,详情请移步 jquery animate妙用
如果问题现在再变一下,要求在3s内,将一个div上显示的数字从快到慢地(匀减速)从0变到100,同时将它的left值从0变到785. 这怎么办? 这样我们就需要一个进度信息,然后就知道在时间轴上动画已经走了多长时间,这样就可以抽象出一个t,然后通过自己指定的加速度去控制,当然,我们可以在回掉中传入第二个参数t。不过现在,我们并不打算这么做,好了,我们回过偷来好好想一下,其实无论对于什么动画,无论是属性动画还是数据动画,业务最关心的是每一帧动画的进度,一旦有了这个进度信息,我们就知道该怎么做了,所以,我们完全可以不关心到底有什么数据从什么样的范围变化,这是业务层关心的,animate只关心动画的进度,比如1/10,1/5, 用户根据这个进度决定自己的操作,比如最初的老问题可以这么解决:
animate(3000,
function
(time){
var
progress=time/3000;
div.text(100*progress).css(
"left"
,785*progress)
})
animate本身根本不需要知道有哪些数据变化,以及他们的变化范围,它只提供一个进度(time表示当前动画已经走了多久了),而用户根据自己的业务去做处理就行!
这样是最合理的,就是可能用户在执行回调用的时候需要依赖/修改/保存一些数据以供下一帧使用,我们可以提供一种重载,添加一个可选的参数arguments, 然后在每一帧执行回调时传递给回调函数,下面看源码吧:
$.animate=
function
(speed,arguments,callback){
if
(speed<30) speed=30;
//可能调用时用户没有传arguments
var
f=callback||arguments
var
argvs=callback&&(arguments.speed=speed)&&arguments;
var
frameCount=Math.ceil(speed/1000)*30
var
t=speed/frameCount;
var
ct=0,timer;
function
proxy(){
ct+=t;
if
(ct>speed) {clearInterval(timer); f(speed,argvs);
return
;}
f(ct,argvs);
}
timer=setInterval(proxy,t)
}
需要说明的是,我们是固定1s钟分30帧,而为了更合理的利用浏览器渲染,提高性能,我们通过requestAnimationFrame和cancelAnimationFrame来替换直接将帧数写死,不认识这两个面孔的请直接百度吧。下面试最终实现:
window.requestAnimationFrame =requestAnimationFrame||
function
(callback){setTimeout(callback, 1000/60)}
window.cancelAnimationFrame =cancelAnimationFrame||
function
(id) {clearTimeout(id)}
$.animate1=
function
(speed,arguments,callback){
if
(speed<30) speed=30;
var
f=callback||arguments
var
argvs=callback&&(arguments.speed=speed)&&arguments;
function
proxy(c){
if
(c>speed) { f(speed,argvs);
return
;}
f(c,argvs);
requestAnimationFrame(proxy);
}
requestAnimationFrame(proxy)
}
|
animate就讨论到这里,下次有时间我把自己实现的旋转插件和附带的一些示例再分享,这些代码中都用到了现在以及之前讨论的一些东西。