requestAnimationFrame用法解析

背景

在项目中,前端经常需要处理后端返回的大数据,如果一次性将所有的数据渲染到dom,那么就会卡顿,所以有的时候需要分批加载处理。现在来举例模拟实现

一. 应用场景示例

const dataList = [{
  id:1
},{
  id:2
},{
  id:3
},{
  id:4
},{
  id:5
},{
  id:6
},{
  id:7
},{
  id:8
},{
  id:9
},{
  id:10
},{
  id:11
},{
  id:12
},{
  id:13
},{
  id:14
},{
  id:15
},{
  id:16
}]

const resData = [];
const limit = 3;
const totalPages = Math.ceil(dataList.length / limit);

const renderData = () => {  
  let currentPage = 0;  
  
  const renderPage = () => {  
    if (currentPage >= totalPages) {  
      return; // 确保退出条件在正确的位置  
    }  
  
    const start = currentPage * limit;  
    const end = Math.min(start + limit, dataList.length); // 防止越界  
    for (let i = start; i < end; i++) {  
      resData.push(dataList[i].id); // 直接使用 .id,不需要可选链  
    }
    console.log(resData,'resData')
    currentPage++;  
  
    if (currentPage < totalPages) {  
      requestAnimationFrame(renderPage);
    }  
  };  
  
  renderPage();  
};

renderData()

二. requestAnimationFrame是什么?

requestAnimationFrame是一种在浏览器中实现动画循环的技术,它通过定时器机制来周期性地调用指定的回调函数,以实现网页动画的效果。与传统的setInterval和setTimeout不同,requestAnimationFrame具有更好的浏览器兼容性、更精确的定时控制和更优秀的性能表现。

使用requestAnimationFrame可以将动画的每一帧绘制操作封装为一个回调函数,并将这个回调函数传递给requestAnimationFrame函数。当浏览器准备进行下一帧绘制时,会自动调用这个回调函数,从而实现了动画的循环。

三. 和setTimeout和setInterva对比

工作原理:
setTimeout:这个函数会将要执行的代码或函数放入事件循环队列中,等待当前代码执行完毕后,再等待指定的时间后执行一次。如果设置了定时器,那么每隔一定时间就会执行一次代码,直到 clearTimeout 被调用或窗口被关闭。

setInterval:与 setTimeout 类似,setInterval 也会将要执行的代码或函数放入事件循环队列中,但它在指定的时间间隔后会一直重复执行,直到 clearInterval 被调用或窗口被关闭。也就是说,setInterval 会不断地调用函数,直到被取消。

requestAnimationFrame:这个函数的工作原理与 setInterval 和 setTimeout 略有不同。它会将回调函数加入到浏览器下一次重绘之前要执行的队列中。这样做的目的是为了确保动画的流畅度,因为浏览器会自动优化这个API,只在浏览器处于激活状态并且页面处于可见状态时才会执行回调函数。此外,requestAnimationFrame 会根据系统的刷新率来自动匹配时间间隔,从而确保每帧动画的间隔时间尽可能地准确。

区别:
执行时机:requestAnimationFrame是由浏览器提供的API,它会在浏览器下一次重绘之前执行回调函数。这意味着它能够确保动画的流畅度,并且能够自动匹配系统的刷新率。相比之下,setInterval和setTimeout会在指定的时间间隔后执行回调函数,无论浏览器是否处于激活状态或正在进行其他操作。

性能优化:requestAnimationFrame由浏览器自动优化,只在浏览器处于激活状态并且页面处于可见状态时才会执行回调函数。这可以节省CPU、GPU和内存的使用,特别是在移动设备上。相比之下,setInterval和setTimeout不会自动优化,如果页面处于隐藏或不可见状态,它们会继续执行回调函数,这可能会导致资源的浪费。

回调函数执行时间:requestAnimationFrame的回调函数会在浏览器下一次重绘之前执行,因此它能够确保回调函数的执行时间相对准确。相比之下,setInterval和setTimeout的回调函数执行时间取决于浏览器事件循环中的队列和执行时间,因此可能会有一定的延迟。

停止操作:requestAnimationFrame的回调函数只会在浏览器下一次重绘之前执行一次,因此可以通过清除队列中的回调函数来停止操作。相比之下,setInterval和setTimeout会不断地执行回调函数,直到clearInterval或clearTimeout被调用或关闭页面为止。

函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。

间隔时间
setInterval的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行

HTML5标准规定,setTimeout的最短时间间隔是4毫秒;setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒

大多数电脑显示器的刷新频率是60HZ,大概相当于每秒钟重绘60次。因此,最平滑的动画效的最佳循环间隔是1000ms/60,约等于16.6ms

为了节电,对于那些不处于当前窗口的页面,浏览器会将时间间隔扩大到1000毫秒。另外,如果笔记本电脑处于电池供电状态,Chrome和IE10+浏览器,会将时间间隔切换到系统定时器,大约是16.6毫秒

应用场景
setTimeout:可用于在网页加载后延迟执行某些操作,例如加载页面内容、初始化组件等。也可用于定时触发某些操作,例如定时发送数据、定时检查任务等。

setInterval:常用于需要周期性执行的操作,例如定时更新数据、定时触发事件等。在web端,如果列表需要定时更新,可以使用setInterval来定时获取列表的请求。另外,如果需要在某一特定情况下清除定时任务,可以使用clearInterval来停止定时器。

requestAnimationFrame:主要用于实现流畅的动画效果。它会在浏览器下一次重绘之前执行指定的函数,避免了频繁的重绘导致的性能问题。requestAnimationFrame会自动匹配系统的刷新率,从而确保每帧动画的间隔时间尽可能地准确。在需要反复触发的情况下,使用requestAnimationFrame可以避免连续调用导致的相互干扰。

四. 直观对比

现在分别使用 setInterval、setTimeout 和 requestAnimationFrame 这三个方法制作一个简单的进制度效果

<div style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;" id="div">0%</div>

<button id="btn">run</button>

<script>
    var timer;
    var btn = document.getElementById('btn')
    var myDiv = document.getElementById('div')
    
    // 1.requestAnimationFrame
    btn.onclick = function () {
        myDiv.style.width = '0';
        cancelAnimationFrame(timer);
        timer = requestAnimationFrame(function fn() {
            if (parseInt(myDiv.style.width) < 500) {
                myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
                myDiv.innerHTML = parseInt(myDiv.style.width) / 5 + '%';
                timer = requestAnimationFrame(fn);
            } else {
                cancelAnimationFrame(timer);
            }
        });
    }

    // 2.setInterval
    btn.onclick = function () {
        clearInterval(timer);
        myDiv.style.width = '0';
        timer = setInterval(function () {
            if (parseInt(myDiv.style.width) < 500) {
                myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
                myDiv.innerHTML = parseInt(myDiv.style.width) / 5 + '%';
            } else {
                clearInterval(timer);
            }
        }, 16);
    }

    // 3.setTimeout
    btn.onclick = function () {
        clearTimeout(timer);
        myDiv.style.width = '0';
        timer = setTimeout(function fn() {
            if (parseInt(myDiv.style.width) < 500) {
                myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
                myDiv.innerHTML = parseInt(myDiv.style.width) / 5 + '%';
                timer = setTimeout(fn, 16);
            } else {
                clearTimeout(timer);
            }
        }, 16);
    }
</script>

对比来看,还是requestAnimationFrame用着比较流畅

五. 总结

这里为了讲解requestAnimationFrame的用法而举例的处理大数据的方法,但是实际项目中,处理大数据的解决方案还是应用虚拟列表的形式。
其用法可参考如下文章:

我的发!被后端五万条数据爆破我是怎么处理的

听说你还不会虚拟列表?原谅我来晚了

其它参考文章

前端开发必备:requestAnimationFrame、setInterval、setTimeout——功能解析与优劣对比

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值