ver 1.0 2013-3-19
页面上的动画怎么做?
首先定义,这里指的动画,是“移动到这里那里,变大变小一点”之类的补间动画(tween),而不是名侦探可能之类的TV动画什么的。
直到目前,(css除外)对于js和html来说,也没有一个非常直接的方式去做一个动画。
不过人们还是很聪明的,大家想到了一个方式,就是不断的修改一个DOM元素的style,比如让left从0逐渐变成100,然后视觉上好像是往右慢慢地移动了过去的效果。
这里有一个例子:
【demo页面:原生 setinterval】
1 2 3 4 5 6 7 8 | var timer=setInterval(function(){ var w= parseInt( a.style.width.replace(/px$/,''),10); if (w<=400){ a.style.width=(w+1)+'px'; }else{ clearInterval(timer); } },20); |
可以看到,这里用一个非常简单的方式实现了一个动画,就是不断地检测元素的宽度,不到400就一直加1。
这里的一个精髓就是用到了setInterval,setInterval就是每隔多少时间执行一次相应的代码,从而让我们的动画看起来是连续的。
这里有个问题,就是setInterval并不是完全精准的,你定义20ms执行一次,浏览器可能19或者21或者22ms执行一次,这都是有可能的。
所以,可能动画在不同电脑\不同浏览器上面的速度会有比较明显的差别,这也的确令人比较头痛。
jquery作为js库中很常用的一个,自带了动画功能,使得很多人免去自己写动画的苦恼。那么jquery是怎么处理动画的呢?会不会遇到和上面一样的问题?
jquery的animate
说道动画效果,大家最熟悉的应该就是jquery的animate了
这次要做动画效果的调研,就先找了下jquery.animate的原理:
通过setInterval每隔一定时间(jQuery中是13ms)进行一次位移运算,直到当前时间与初始时间的差值大于动画时长,这就是jQuery.animate的执行过程。
1 2 3 4 5 6 7 | jQuery.fx.interval = 13; jQuery.fx.start = function() { if ( !timerId ) { timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); } }; |
其他过程不再详细叙述
大概过程就是,
jquery动画开始之后,
- setinterval定时执行,
- 执行的时候判断下当前时间,
- 再通过easing函数,
- 来决定动画的完成比,
- 然后就改变dom的style。
1 2 3 4 5 6 7 8 9 10 11 | var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) temp = remaining / animation.duration || 0, percent = 1 - temp, index = 0, length = animation.tweens.length; for ( ; index < length ; index++ ) { animation.tweens[ index ].run( percent ); } |
代码来自 jquery1.9.1
来看这一段,这一段是在setInterval里面的,里面会计算目前时间和结束、开始时间的比例,从而计算得出一个percent值,设置css的时候也会根据这个percent来确定各种属性。
基本上,这样就不会出现有的浏览器快,有的浏览器慢的情况了,最多是有的卡一点,有的流畅一点。
更高端,更流畅?
是的,我们希望页面上的动画高端大气上档次,流畅如油……
怎么办?
不要着急,web发展了这么些年了(好像也没多少年),新的标准、接口如春笋般涌现。自然有人注意到setInterval实现的动画并不是很完美,所以有两个与动画相关的东西出来咯
CSS3 Transitions
没错,css3有许多新特性,这个也是,transition规定了当dom元素某些值变化的时候,要怎么样(渐渐)变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | .box { width: 300px; height: 300px; -webkit-transition: width 2s ease, height 2s ease; -moz-transition: width 2s ease, height 2s ease; -o-transition: width 2s ease, height 2s ease; -ms-transition: width 2s ease, height 2s ease; transition: width 2s ease, height 2s ease; } .box-change { width: 400px; height: 400px; } |
1 | $(".box").toggleClass("box-change"); |
代码来自:http://www.impressivewebs.com/css3-transitions-javascript/
以上就可以实现高宽动画形式变化了。
(做各种浏览器兼容加了好多比较重复的东西)
transation的好处:
基本上js只要trigger一下就好,后续的浏览器帮你做了
requestAnimationFrame
requestAnimationFrame方法是 给 基于脚本的需要进行重采样(多次绘画)的动画 给用户代理发起的请求。
浏览器接到请求后,会放入一个队列,然后依次按照合适的频率执行。
基本上,RAF可以用来替代setInterval
有什么好处呢?
setInterval中的延时很难设置,太小了可能让浏览器占用过多资源或者死掉
太大了可能让动画不流畅
而让浏览器来接管这个频率设定,就可以平衡机器性能和动画表现。
来看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function animate(element, name, from, to, time) { time = time || 800; // 默认0.8秒 var startTime = new Date; function go(timestamp) { var progress = timestamp - startTime; if (progress >= time) { element.style[name] = to + 'px'; return; } var now = (to - from) * (progress / time); element.style[name] = now.toFixed() + 'px'; requestAnimationFrame(go); } element.style[name] = from + 'px'; requestAnimationFrame(go); } |
代码来自:http://otakustay.com/animation-and-requestanimationframe/
整合
以上的css3 transition和RAF,直接用也是略麻烦,目前两者都有jquery插件形式的实现,方便使用。
jquery.transit:整合transation,使用的时候,用$.transation()即可,参数与animate基本一致。
jquery-requestAnimationFrame:将animate的更新改用requestAnimationFrame来实现,直接添加文件后使用animate即可(也有浏览器不支持此接口时的fallback)
性能比较
这里采用原生jquery、和以上两个插件来实现相同的动画,比较之间的差异。
jquery原生动画(页面链接):
jquery + jquery.transit(页面链接):
frames(帧率)可以看到更加平滑,内存也少了点,js事件变得很少,浏览器事件处理(events里面最后一行)也有所减少
jquery + jquery-requestAnimationFrame(页面链接)
可以看到,frames也是很平滑,memrory和jquery原生占用差不多(略多一点),events比原生少了许多。
总结
介绍了transition和RAF,这里来比较一下两者特点
transition | CSS3特性,动画中间不需要js介入。 性能好 只能完成CSS3 transition中有定义的动画 |
requestAnimationFrame | 高级浏览器接口 可以用类似jquery原生的方式来进行动画 性能好 内存占用比transation高 可以完成比较复杂的动画(相对于transation) 网页处于不活动的时候,会停止。 |