资讯专栏INFORMATION COLUMN

神奇的requestAnimationFrame

Olivia / 3072人阅读

摘要:方法用于取消先前安排的一个动画帧更新的请求。当调用时,浏览器会设置该指向的回调函数的为。无论该回调函数是否在动画帧请求回调函数列表中,它的都会被设置为。

引入

计时器一直是JavaScript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。

如何保证流畅呢?登登登登....该requestAnimationFrame登场了~~

16.7ms的由来

大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.7ms

settimeout->OUT

同样的,显示器16.7ms刷新间隔之前发生了其他绘制请求(setTimeout),导致所有第三帧丢失,继而导致动画断续显示(堵车的感觉),这就是过度绘制带来的问题。不仅如此,这种计时器频率的降低也会对电池使用寿命造成负面影响,并会降低其他应用的性能。

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

但...

1、即使向其传递毫秒为单位的参数,它们也不能达到ms的准确性。这是因为javascript是单线程的,可能会发生阻塞。

2、没有对调用动画的循环机制进行优化。

3、没有考虑到绘制动画的最佳时机,只是一味地以某个大致的事件间隔来调用循环。

...OUT requestAnimationFrame登场

requestAnimationFrame不需要使用者指定循环间隔时间,浏览器会基于当前页面是否可见、CPU的负荷情况等来自行决定最佳的帧速率,跟着浏览器的绘制走,如果浏览设备绘制间隔是16.7ms,那我就这个间隔绘制;如果浏览设备绘制间隔是10ms, 我就10ms绘制。
这样自然就合理地使用CPU,不会存在过度绘制的问题,动画不会掉帧,自然流畅的说~~

内部是这么运作的:

浏览器(如页面)每次重绘,就会通知我(requestAnimationFrame):嗨,我要重绘了,你可以跟我一起重绘哦!

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

1.就算很多元素需要重绘,浏览器只要通知一次就可以了。而setTimeout貌似是多个独立绘制。

2.页面最小化了,或者被Tab切换关灯了。页面是不会重绘的,自然,requestAnimationFrame也不会洗澡的(没通知啊)。页面绘制全部停止,资源高效利用。

总结:

setTimeoutsetInterval都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

特点

requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率

在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量

requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销

名词解释

动画帧请求回调函数列表
每个Document都有一个动画帧请求回调函数列表,该列表可以看成是由< handle, callback>元组组成的集合。其中handle是一个整数,唯一地标识了元组在列表中的位置;callback是一个无返回值的、形参为一个时间值的函数(该时间值为由浏览器传入的从1970年1月1日到当前所经过的毫秒数)。 刚开始该列表为空。
Document
Dom模型中定义的Document节点
Active document
浏览器上下文browsingContext中的Document被指定为active document
browsingContext
即浏览器上下文。
浏览器上下文是呈现document对象给用户的环境。 浏览器中的1个tab或一个窗口包含一个顶级浏览器上下文,如果该页面有iframe,则iframe中也会有自己的浏览器上下文,称为嵌套的浏览器上下文。
页面可见
当页面被最小化或者被切换成后台标签页时,页面为不可见,浏览器会触发一个 visibilitychange事件,并设置document.hidden属性为true;切换到显示状态时,页面为可见,也同样触发一个 visibilitychange事件,设置document.hidden属性为false
队列
浏览器让一个单线程共用于执行javascrip和更新用户界面。这个线程通常被称为“浏览器UI线程”。 浏览器UI线程的工作基于一个简单的队列系统,任务会被保存到队列中直到进程空闲。一旦空闲,队列中的下一个任务就被重新提取出来并运行。这些任务要么是运行javascript代码,要么执行UI更新,包括重绘和重排。

API接口

Window对象定义了以下两个接口:

partial interface Window {
  long requestAnimationFrame(FrameRequestCallback callback);
  void cancelAnimationFrame(long handle);
};
requestAnimationFrame

1.requestAnimationFrame方法用于通知浏览器重采样动画。

当requestAnimationFrame(callback)被调用时不会执行callback,而是会将元组< handle,callback>插入到动画帧请求回调函数列表末尾(其中元组的callback就是传入requestAnimationFrame的回调函数),并且返回handle值,该值为浏览器定义的、大于0的整数,唯一标识了该回调函数在列表中位置。

2.每个回调函数都有一个布尔标识cancelled,该标识初始值为false,并且对外不可见。
3.在后面的“处理模型” 中我们会看到,浏览器在执行“采样所有动画”的任务时会遍历动画帧请求回调函数列表,判断每个元组的callback的cancelled,如果为false,则执行callback。

cancelAnimationFrame

1、cancelAnimationFrame 方法用于取消先前安排的一个动画帧更新的请求。
2、当调用cancelAnimationFrame(handle)时,浏览器会设置该handle指向的回调函数的cancelled为true。
无论该回调函数是否在动画帧请求回调函数列表中,它的cancelled都会被设置为true。
3.如果该handle没有指向任何回调函数,则调用cancelAnimationFrame 不会发生任何事情。
4.注意:在requestAnimationFrame的callback内部执行cancelAnimationFrame不能取消动画

处理模型

当页面可见并且动画帧请求回调函数列表不为空时,浏览器会定期地加入一个“采样所有动画”的任务到UI线程的队列中。

使用

requestAnimationFrame的用法与setTimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行。

requestID = requestAnimationFrame(callback);
完美兼容


如果想要简单的兼容,可以这样子:

window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          function( callback ){
            window.setTimeout(callback, 1000 / 60);
          };
})();

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

(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"] ||    // Webkit中此取消方法的名字变了
                                          window[vendors[x] + "CancelRequestAnimationFrame"];
        }
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16.7 - (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);
        };
    }
}());

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90021.html

相关文章

  • 神奇requestAnimationFrame

    摘要:方法用于取消先前安排的一个动画帧更新的请求。当调用时,浏览器会设置该指向的回调函数的为。无论该回调函数是否在动画帧请求回调函数列表中,它的都会被设置为。 引入 计时器一直是JavaScript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化...

    plokmju88 评论0 收藏0
  • 神奇requestAnimationFrame

    摘要:方法用于取消先前安排的一个动画帧更新的请求。当调用时,浏览器会设置该指向的回调函数的为。无论该回调函数是否在动画帧请求回调函数列表中,它的都会被设置为。 引入 计时器一直是JavaScript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化...

    周国辉 评论0 收藏0
  • 还在为无缝滚动而烦恼?是时候彻底get到这个知识点了

    摘要:最近一直在忙公司炒股大赛的页面,终于在昨天把他给上线了。刚开始学习的时候,真心觉得无缝滚动是一个神奇的功能。原理假如需要无缝滚动的个元素是一个中的个。我们将控制在容器中滚动。这样无缝滚动就已经实现了。 最近一直在忙公司炒股大赛的页面,终于在昨天把他给上线了。一个看似简单的页面,做起来才知道其中的艰辛,暗藏深坑。由于直接使用jquery来写页面逻辑,因此要比想象中复杂很多。无论是从布局,...

    stormjun 评论0 收藏0
  • three.js中文文档 1.创建场景

    摘要:创建场景标签本节目标是为做简介。我们从使用旋转立方体来搭建场景开始。我们现在创建了场景,摄像机和渲染器。我们需要来创建立方体。这会导致摄像机和立方体内部重叠。这会创建一个让渲染器每秒绘制一帧的循环。结果恭喜你现在创建好了第一个应用。 1.创建场景 标签: three.js 本节目标是为 three.js 做简介。我们从使用旋转立方体来搭建场景开始。如果遇到困难需要帮助,页面底部有可参考...

    Michael_Lin 评论0 收藏0
  • 基于Canvas动画基本原理与数理分析

    摘要:注以下所有代码托管到动画的数理分析有了前面的基础知识,现在我们就会想如果我们能够在每秒帧,内渲染张图像,并且每一张图像的内容发生微调,那么在秒钟整个画面就会产生动画效果了。 什么是动画? 就像思考哲学问题无法回避思维和存在的关系一样,制作动画同样无法逃避的问题是动画的原理是什么?这里提一句题外话,任何原理的东西通常难以让你短期拾掇成果,但在隐约的未来会起到难以置信的效果,不信就看接下来...

    genedna 评论0 收藏0

发表评论

0条评论

Olivia

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<