canvas雨滴绘制之(四)requestAnimationFrame

本文深入探讨requestAnimationFrame函数的工作原理及其在浏览器动画中的应用。通过对比传统定时器方法,阐述了requestAnimationFrame如何提高动画性能,减少资源消耗,并保持动画流畅。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 fallback
window.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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值