本文首先介绍什么是requestAnimationFrame,然后深入讲述requestAnimationFrame的动画循环。文章很详细,相信对大家学习requestAnimationFrame有一定的参考价值。有需要就来看看吧。
一、初识requestAnimationFrame
RequestAnimationFrame解决了浏览器不知道javascript动画何时开始以及最佳循环间隔的问题。它遵循浏览器的绘图。如果浏览器的绘制间隔是16.7ms,就在这个间隔进行绘制;如果浏览器绘制间隔为10ms,则绘制间隔为10ms。这样就不会出现过画的问题,动画也不会丢帧。
下面是它的内部工作原理:
浏览器每次重绘页面,都会通知requestAnimationFrame
这是一种非常有效的资源利用方式。
怎么说呢?
有以下两点:
1.即使要执行许多requestAnimationFrame(),浏览器也只需要通知它们一次。而setTimeout是一些独立的绘图。
2.一旦页面不在当前页面之外(比如页面被最小化),页面就不会被重绘,自然也不会触发requestAnimationFrame(因为没有通知)。所有页面绘制停止和资源都得到有效利用。
编辑
二. 动画的循环间隔
写动画循环的关键是知道延迟时间多长合适。一方面,循环时间必须足够短,才能保证动画效果更加流畅;另一方面,循环应该足够长,以确保浏览器能够呈现更改。大多数显示器的刷新频率为60Hz,相当于每秒60次重绘。大多数浏览器限制重绘操作不超过显示器的重绘频率,因为即使超过这个频率,用户体验也不会得到改善。
所以最流畅动画的最佳循环间隔是1000ms/60,大约是17 ms,在这个循环间隔重绘的动画是流畅的,因为这个速度最接近浏览器的最大速度限制。为了适应17ms的循环间隔,可能需要对多个动画进行约束,以免结束得太快。
虽然与使用多组setTimeout()相比,在动画循环中使用setInterval()效率更高。但是setTimeout()和setInterval()都不是很准确。它们传入的第二个参数实际上只指定了动画代码加入浏览器UI线程队列等待执行的时间。如果队列前面已经添加了其他任务,动画代码将在前面的任务完成后执行。如果UI线程很忙,比如处理用户操作,即使代码被添加到队列中,也不会立即执行。
因此,知道何时绘制下一帧是保证动画流畅的关键。但是,面对不精确的setTimeout()和setInterval(),开发者仍然无法保证浏览器按时绘制下一帧。
以下是几个浏览器的计时器精度:
E8及以下浏览器:15.6ms;
IE9及以上浏览器:4 ms
而火狐:10ms
铬:4毫秒.
更复杂的是,浏览器开始限制背景标签或者非活动标签的计数器。因此,即使你优化了周期间隔,你可能仍然只能达到预期的效果。
三. requestAnimationFrame()
Mozilla的罗伯特奥卡拉汉(Robert OCallahan)指出,CSS变换动画的优势在于浏览器知道动画何时开始,因此会计算出正确的循环间隔,并在适当的时候刷新UI。对于JavaScript动画,浏览器无法知道何时开始。
因此,罗伯特奥卡拉汉的计划是创建一个新方法mozRequestAnimationFrame(),它可以告诉浏览器一些代码将被动画化。这样浏览器在运行一些代码后就可以进行适当的优化。
与setTimeout()和setInterval()方法不同,requestAnimationFrame()不需要调用者指定帧率,浏览器会自行决定最佳帧效率。
requestAnimationFrame()方法接收一个参数,即在重绘屏幕前调用以个函数。这个函数负责改变下一次重绘时的数字正射影像图样式。为了创建动画循环,可以像使用setTimeout()一样,把多个对requestAnimationFrame()的调用连缀起来。
如:
函数drawFrame() {
窗户。requestanimationframe(绘制帧);
//动画代码.
}
窗户。requestanimationframe(绘制帧);
三. requestAnimationFrame()的兼容性
3.1 requestAnimationFrame()的兼容性封装:
由于mozRequestAnimationFrame()是HTML5的新功能,目前各大浏览器的支持情况各异。如果希望代码具备更好的跨平台性,可以考虑使用下面的代码实现各平台兼容性:
如果(!window.requestAnimationFrame) {
窗户。requestanimationframe=(window。webkitrequestanimationframe | |
窗户。mozrequestanimationframe | |
窗户。orequestanimationframe | |
窗户。msrequestanimationframe | |
函数(回调){
var self=this,start,finish
返回窗口。settimeout(function(){
start=new Date();
回调(开始);
finish=new Date();
self.timeout=1000/60 -(完成-开始);
},自我。暂停);
});
}
这段代码先检查了window.requestAnimationFrame函数的定义是否存在。如果不存在,就遍历已知的各种浏览器实现并替代该函数。如果还是找不到一个与浏览器相关的实现,它最终会采用基于Java脚本语言定时器的动画以每秒60帧的间隔调用设置超时函数。
mozRequestAnimationFrame()会接收一个时间码(从1970年一月一日起至今的毫秒数),表示下一次重绘的实际发生时间。这样,mozRequestAnimationFrame()就会根据这个时间码设定将来的某个时刻进行重绘。
但是webkitRequestAnimationFrame()和msRequestAnimationFrame()不会给回调函数传递时间码,因此无法知道下一次重绘将发生在什么时间。
如果要计算两次重绘的时间间隔,火狐中可以使用既有的时间码,而在铬和工业管理学(工业工程)则可以使用不太精确地日期()对象。
3.2 cancelRequestAnimFrame()的兼容性封装:
万维网路联盟(环球网Consortium简称W3C)也提供了cancelRequestAnimationFrame()方法,用于取消回调函数requestAnimationFrame()方法会返回一个对象,用做标识回掉函数身份。若要取消回调函数的执行,可将其传给cancelRequestAnimationFrame()。
窗户。cancelrequestanimframe=(function(){
返回窗口。cancelanimationframe | |
窗户。webkitcancelrequestanimationframe | |
窗户。mozcancelrequestanimationframe | |
窗户。ocancelrequestanimationframe | |
窗户。mscancelrequestanimationframe | |
清除超时
} )();
3.3 requestAnimationFrame()升级版封装方法:
另外还有一种更优雅的requestAnimationFrame()的兼容性封装方法:
(函数(){
var last time=0;
var vendors=[ms , moz , webkit , o ];
for(var x=0;x厂商。长度!window . requestanimationframex){
窗户。RequestAnimationFrame=window[vendors[x] RequestAnimationFrame ];
窗户。CancelAnimationFrame=window[vendors[x] CancelAnimationFrame ]| | window[vendors[x] CancelRequestAnimationFrame ];
}
如果(!window.requestAnimationFrame)
窗户。requestanimationframe=function(callback,element) {
var currTime=新日期()。getTime();
var timeToCall=Math.max(0,16-(当前时间-上次时间));
var id=窗口。settimeout(function(){ callback(curr time要调用的时间);},
打电话的时间);
last time=curr time调用时间;
返回id;
};
如果(!window.cancelAnimationFrame)
窗户。cancelanimationframe=函数(id){
清除超时(id);
};
}());
总结
以上就是这篇文章的全部内容,希望能对大家的学习或者工作带来一定的帮助,如果有疑问大家可以留言交流。