requestAnimationFrame性能测试

requestAnimationFrame:每次重绘最多只调用一次回调函数
测试开启/关闭requestAnimationFrame的监听事件调用次数差异:
先说结论:存在约8倍的调用次数差距!

requestAnimationFrame使用与否的次数差距

本次测试代码为drag事件

const source = document.getElementById("draggable");
    let enqueued = false
    let times = 0
    source.addEventListener("drag", (event) => {
    });

    let timeNow = null;
    source.addEventListener("dragstart", (event) => {
        if(!enqueued) {
            timeNow = new Date();
        }
    });    
    const target = document.getElementById("droptarget");
    target.addEventListener("dragover", (event) => {
        // if (!enqueued) {
        //     enqueued = true
        //     requestAnimationFrame(() => {
                // console.log(event);
                console.log(times++);
                // enqueued = false
        //     })
        // }
        event.preventDefault();
    }, false);
        target.addEventListener("drop", (event) => {
        console.log('done');
        console.log(new Date() - timeNow);
    });

不开启requestAnimationFrame,测试drag次数:

requestAnimationFrame性能测试_第1张图片

requestAnimationFrame性能测试_第2张图片
requestAnimationFrame性能测试_第3张图片
requestAnimationFrame性能测试_第4张图片

requestAnimationFrame性能测试_第5张图片
这是未开启requestAnimationFrame的dragstart到drop时间间隔内的 dragover次数,它们次数/时间间隔分别为:0.4997、0.4705、0.4550、0.5110、0.4148。
平均为:0.4761

const source = document.getElementById("draggable");
    let enqueued = false
    let times = 0
    source.addEventListener("drag", (event) => {
    });

    let timeNow = null;
    source.addEventListener("dragstart", (event) => {
        if(!enqueued) {
            timeNow = new Date();
        }
    });    
    const target = document.getElementById("droptarget");
    target.addEventListener("dragover", (event) => {
        if (!enqueued) {
            enqueued = true![请添加图片描述](https://img-blog.csdnimg.cn/96613896661944738b1deb7e85b07796.png)

            requestAnimationFrame(() => {
                console.log(event);
                console.log(times++);
                enqueued = false
            })
        }
        event.preventDefault();
    }, false);
    target.addEventListener("drop", (event) => {
        console.log('done');
        console.log(new Date() - timeNow);
    });

requestAnimationFrame性能测试_第6张图片
requestAnimationFrame性能测试_第7张图片
requestAnimationFrame性能测试_第8张图片
requestAnimationFrame性能测试_第9张图片
requestAnimationFrame性能测试_第10张图片
加上requestAnimationFrame之后他们的次数/时间间隔为:
0.0597、0.0592、0.0596、0.0581、0.0595
平均为:0.0592

前后差距:0.4761/0.0592约等于8倍的调用次数差距!

requestAnimationFrame与settimeout、setinterval的区别

无论 setInterval()还是 setTimeout()都是不能保证时间精度的。

作为第二个参数的延时 只能保证何时会把代码添加到浏览器的任务队列,不能保证添加到队列就会立即运行。如果队列前面还有其他任务,那么就要等这些任务执行完再执行。简单来讲,这里毫秒延时并不是说何时这些代码会执 行,而只是说到时候会把回调加到任务队列。如果添加到队列后,主线程还被其他任务占用,比如正在 处理用户操作,那么回调就不会马上执行。

requestAnimationFrame()方法接收一个参数,此参数是一个要在重绘屏幕前调用的函数。

它解决了浏览器不知道 JavaScript 动画何时开始的问题, 以及最佳间隔是多少的问题。
传给 requestAnimationFrame()的函数实际上可以接收一个参数,此参数是一个 DOMHighRes- TimeStamp 的实例(比如 performance.now()返回的值),表示下次重绘的时间。这一点非常重要: requestAnimationFrame()实际上把重绘任务安排在了未来一个已知的时间点上,而且通过这个参数 告诉了开发者。基于这个参数,就可以更好地决定如何调优动画了。

与 setTimeout()类似,requestAnimationFrame()也返回一个请求 ID,可以用于通过另一个 方法 cancelAnimationFrame()来取消重绘任务。

let requestID = window.requestAnimationFrame(() => {
  console.log('Repaint!');
})
window.cancelAnimationFrame(requestID);

requestAnimationFrame如何实现节流

支持这个方法的浏览器实际上会暴露出作为钩子的回调队列。所谓钩子(hook),就是浏览器在执行下一次重绘之前的一个点。这个回调队列是一个可修改的函数列表,包含应该在重绘之前调用的函数。每次调用 requestAnimationFrame()都会在队列上推入一个回调函数,队列的长度没有限制。

可以保证每次重绘最多只调用一次回调函数。这是一个非常好的节流工具。在频繁执行影响页面外观的代码时(比如滚动事件监听器),可以利用这个回调队列进行节流。例如上文的代码

但是重绘是非常频繁的操作,所以加上requestAnimationFrame之后还不算真正的节流,需要配合定时器

 let enabled = true;
 function expensiveOperation() {
      console.log('Invoked at', Date.now());
 }
 window.addEventListener('scroll', () => {
 if (enabled) {
     enabled = false;
     window.requestAnimationFrame(expensiveOperation);
     window.setTimeout(() => enabled = true, 50);
 }
});

你可能感兴趣的:(javascript,前端,开发语言)