当前位置:网站首页>使用 requestAnimationFrame 提升 web 性能

使用 requestAnimationFrame 提升 web 性能

2022-08-10 03:15:00 朝阳39

requestAnimationFrame 与延时执行函数 setTimeout 类似,但不用设置时间,即可将操作延迟至下一次网页重绘时执行。

requestAnimationFrame 的用法

延迟到下一帧执行

requestAnimationFrame(回调函数)

功能:将回调函数,延迟到下一帧(下一次网页重绘)前执行。

每一帧都执行

在回调函数内再次调用 requestAnimationFrame()

requestAnimationFrame(function update() {
    
  console.log("执行了更新!");
  // requestAnimationFrame 的回调函数默认只会被调用一次,如果希望每帧都执行,则每帧都需要调用
  requestAnimationFrame(update);
});

取消执行

requestAnimationFrame(回调函数) 会返回一个整数(定时器的编号),将其传给cancelAnimationFrame(定时器的编号) 可取消回调函数的执行,如:

// 定义定时器
let timer1 = requestAnimationFrame(回调函数)
// 取消定时器
cancelAnimationFrame(timer1)

requestAnimationFrame 的常见应用场景

提升 web 性能

使用 requestAnimationFrame 替换 setTimeout 和 setInterva 可提升web性能。

setTimeout 和 setInterva 实现动画的缺点

  • 时间不精确,通常实际执行时间都要比其设定的时间晚一些。
  • 实现的动画在某些低端机上会出现卡顿、抖动的现象。(不同设备的屏幕刷新频率不同,而 setTimeout 和 setInterva 只能设置一个固定的时间间隔,这个时间和屏幕的刷新时间不同时,就会出现掉帧,而导致页面卡顿、抖动)

requestAnimationFrame 实现动画的优势

requestAnimationFrame 采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

全局监听浏览器滚动事件

// 全局监听浏览器滚动事件
window.onscroll = function () {
    
  requestAnimationFrame(function scroll() {
    
    console.log("页面滚动了");
  });
};

平滑滚动

<button onclick="scrollToTop()">回到顶部</button>
function scrollToTop() {
    
    const c = document.documentElement.scrollTop || document.body.scrollTop;
    if (c > 0) {
    
        window.requestAnimationFrame(scrollToTop);
        window.scrollTo(0, c - c / 8);
    }
}

大量数据的渲染

比如十万条数据的渲染,使用 requestAnimationFrame 分页加载。

//需要插入的容器
let ul = document.getElementById('container')
// 插入十万条数据
let total = 100000
// 一次插入 20 条
let once = 20
//总页数
let page = total / once
//每条记录的索引
let index = 0
//循环加载数据
function loop(curTotal, curIndex) {
    
  if (curTotal <= 0) {
    
    return false
  }
  //每页多少条
  let pageCount = Math.min(curTotal, once)
  window.requestAnimationFrame(function () {
    
    for (let i = 0; i < pageCount; i++) {
    
      let li = document.createElement('li')
      li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)
      ul.appendChild(li)
    }
    loop(curTotal - pageCount, curIndex + pageCount)
  })
}
loop(total, index)

使用 setTimeout 实现的写法如下:

//需要插入的容器
let ul = document.getElementById('container')
// 插入十万条数据
let total = 100000
// 一次插入 20 条
let once = 20
//总页数
let page = total / once
//每条记录的索引
let index = 0
//循环加载数据
function loop(curTotal, curIndex) {
     
  if (curTotal <= 0) {
      
    return false 
  }  
  //每页多少条
  let pageCount = Math.min(curTotal, once) 
  setTimeout(() => {
      
    for (let i = 0; i < pageCount; i++) {
     
      let li = document.createElement('li')    
      li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)    
      ul.appendChild(li)  
    }  
    loop(curTotal - pageCount, curIndex + pageCount) 
  }, 0)
}
loop(total, index)

监控页面的卡顿

比如连续出现 3 个低于 20 的 FPS 即可认为网页存在卡顿。

var lastTime = performance.now()
var frame = 0
var lastFameTime = performance.now()
var loop = function (time) {
    
  var now = performance.now()
  var fs = now - lastFameTime
  lastFameTime = now
  var fps = Math.round(1000 / fs)
  frame++
  if (now > 1000 + lastTime) {
    
    var fps = Math.round((frame * 1000) / (now - lastTime))
    frame = 0
    lastTime = now
  }
  window.requestAnimationFrame(loop)
}

requestAnimationFrame 的优点

  • requestAnimationFrame 会把每一帧中所有的DOM操作集中起来,在一次重绘/回流中完成。

重绘:当某个元素颜色样式发生更改时(如背景颜色、文字颜色),页面也需要更新,浏览器需要重新绘制元素,称为重绘(repaint)。

回流:当页面上的某一个元素的大小或者位置发生更改时,都会影响到与它相邻元素的状况,甚至整个页面的元素状态(位置、元素大小)都需要重新计算和更新。这种操作称为回流(reflow)或者布局(layout)。一个页面至少会有一次回流,就是在页面初始化时。

  • requestAnimationFrame 重绘/回流的时间间隔紧紧跟随浏览器的刷新频率。充分利用了显示器的刷新机制,比较节省系统资源(显示器有固定的刷新频率60Hz/75Hz,即每秒最多重绘60次或75次,requestAnimationFrame的执行紧跟着系统的绘制频率走,便能保证回调函数在屏幕每一次的绘制间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题)。
  • 对隐藏或不可见的元素,requestAnimationFrame不会进行重绘/回流,这意味着更少的CPU、GPU和内存使用量。
  • 当页面被隐藏或最小化时,requestAnimationFrame会停止渲染(setTimeout 和setInterval 仍然在后台执行动画任务),当页面被激活时,动画会从上次停留的地方继续执行,有效节省了CPU、GPU和电力开销。
  • requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用

requestAnimationFrame 的缺点

  • requestAnimationFrame 存在兼容性问题,在不支持的浏览器(如 IE9),需添加兼容代码
  • requestAnimationFrame 使用的主线程,若主线程非常繁忙,requestAnimationFrame的动画效果会大打折扣。

兼容写法

对不支持 requestAnimationFrame 的浏览器(如 IE9),需在代码前添加以下兼容代码:

if(!window.requestAnimationFrame){
    
    var lastTime = 0;
    window.requestAnimationFrame = function(callback){
    
        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;
    }
}
原网站

版权声明
本文为[朝阳39]所创,转载请带上原文链接,感谢
https://sunshinehu.blog.csdn.net/article/details/126247756