canvas雨滴绘制之(四)requestAnimationFrame

来源:互联网 发布:制冷系统仿真软件 编辑:程序博客网 时间:2024/04/29 16:57

canvas雨滴绘制程序:canvas雨滴特效
参考文献1:HTML5探秘:用requestAnimationFrame优化Web动画
参考文献2:使用requestAnimationFrame()优化JavaScript动画性能
参考文献3:CSS3动画那么强,requestAnimationFrame还有毛线用

canva绘制雨滴程序中:requestAnimationFrame(move);

CDN定义:: 此方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。

requestAnimationFrame是什么?

在浏览器动画程序中,我们通常使用一个定时器来循环每隔几毫秒移动目标物体一次,来让它动起来。如今有一个好消息,浏览器开发商们决定:“嗨,为什么我们不在浏览器里提供这样一个API呢,这样一来我们可以为用户优化他们的动画。”

所以,这个requestAnimationFrame()函数就是针对动画效果的API,你可以把它用在DOM上的风格变化或画布动画或WebGL中。

进一步理解

在JavaScript里曾经只有一种方法来设定定时循环任务:setInterval()。如果你想快速的重复有些动作(但又不是像直接调用for循环那样立即执行),你就需要用到定时调度。最常见的就是动画绘制过程,当动画的绘制速度达到每秒钟60帧时,动画会显得非常的流畅。

于是,你需要允许像下面这样一个定时循环任务:

setInterval(function() {  // 做某些动画任务}, 1000/60);

但现在有了一个新的、性能更好的方法可以实现这个任务。requestAnimationFrame()方法。

原理:

相当一部分的浏览器的显示频率是16.7ms,当显示器16.7ms刷新间隔之前发生了其他绘制请求(setTimeout),例如搞个10ms setTimeout,强行要10s显示。显然,这是超负荷的,要想顺利进行,只能让一些帧消失。然而,这是不现实的,于是就有了卡顿,动画断续显示,这就是过度绘制带来的问题。

不仅如此,这种计时器频率的降低也会对电池使用寿命造成负面影响,并会降低其他应用的性能。

这也是为何setTimeout的定时器值推荐最小使用16.7ms的原因(16.7 = 1000 / 60, 即每秒60帧)。

requestAnimationFrame发生了什么?

requestAnimationFrame所做的事情很简单,跟着浏览器的绘制走,如果浏览设备绘制间隔是16.7ms,那我就这个间隔绘制;如果浏览设备绘制间隔是10ms, 我就10ms绘制。这样就不会存在过度绘制的问题,动画不会掉帧,自然流畅~~

内部是这么运作的:
浏览器(如页面)每次要重绘,就会通知(requestAnimationFrame):可以渲染了!

这是资源非常高效的一种利用方式。怎么讲呢?

  1. 就算很多个rAF要一起渲染,浏览器只要通知一次就可以了。而setTimeout貌似是多个独立绘制。
  2. 页面最小化了,或者被Tab切换关灯了。页面是不会render的,自然,rAF也不会render的(没通知)。页面绘制全部停止,资源高效利用。

总结:
为什么requestAnimationFrame()更好?

  • 浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。比如,通过requestAnimationFrame(),JS动画能够和CSS动画/变换或SVG SMIL动画同步发生。

  • 在一个浏览器标签页里运行一个动画,当这个标签页不可见时,浏览器会暂停它,这会减少CPU,内存的压力,节省电池电量。

最简单的一个使用requestAnimationFrame()的例子

function repeatOften() {  // 做某些事情  requestAnimationFrame(repeatOften);}requestAnimationFrame(repeatOften);

这个方法一旦启动,它就会递归的调用自己。

requestAnimationFrame的用法:封装

// shim layer with setTimeout fallbackwindow.requestAnimFrame = (function(){  return  window.requestAnimationFrame       ||          window.webkitRequestAnimationFrame ||          window.mozRequestAnimationFrame    ||          function( callback ){            window.setTimeout(callback, 1000 / 60);          };})();// usage:// instead of setInterval(render, 16) ....(function animloop(){  requestAnimFrame(animloop);  render();})();// place the rAF *before* the render() to assure as close to// 60fps with the setTimeout fallback.

启动和停止

requestAnim
ationFrame 函数能返回一个ID,根据这个ID,你可以停止它的允许,这就像 setTimeout 和 setInterval 的用法一样。下面是一个实际可运行的例子:

var globalID;function repeatOften() {  document.getElementsByTagName("body").appendChild('#');;  globalID = requestAnimationFrame(repeatOften);}$("#start").on("click", function() {  globalID = requestAnimationFrame(repeatOften);});$("#stop").on("click", function() {  cancelAnimationFrame(globalID);});

运行效果

对requestAnimationFrame更牢靠的封装

但是呢,并不是所有设备的绘制时间间隔是1000/60 ms, 以及上面并木有cancel相关方法,所以,就有下面这份更全面的兼容方法:

Opera浏览器的技术师Erik Möller 把这个函数进行了封装,使得它能更好的兼容各种浏览器。你可以读一读这篇文章,但基本上他的代码就是判断使用4ms还是16ms的延迟,来最佳匹配60fps。下面就是这段代码,你可以使用它,但请注意,这段代码里使用的是标准函数,我给它加上了兼容各种浏览器引擎前缀。

(function() {    var lastTime = 0;    var vendors = ['webkit', 'moz'];    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];        window.cancelAnimationFrame =          window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];    }    if (!window.requestAnimationFrame)        window.requestAnimationFrame = function(callback, element) {            var currTime = new Date().getTime();            var timeToCall = Math.max(0, 16 - (currTime - lastTime));            var id = window.setTimeout(function() { callback(currTime + timeToCall); },              timeToCall);            lastTime = currTime + timeToCall;            return id;        };    if (!window.cancelAnimationFrame)        window.cancelAnimationFrame = function(id) {            clearTimeout(id);        };}());

然后,我们就可以以使用setTimeout的调调使用requestAnimationFrame方法啦,IE6也能支持哦!

各种浏览器对requestAnimationFrame的支持情况

谷歌浏览器,火狐浏览器,IE10+都实现了这个函数,即使你的浏览器很古老,上面的对requestAnimationFrame封装也能让这个方法在IE8/9上不出错。2017-12-11:rAF的兼容性性
rAF的兼容性
There’s an issue with requestAnimationFrame on iOS6, the bug still appears to exist in 6.1.3

原创粉丝点击