• 作者:老汪软件技巧
  • 发表时间:2024-08-26 17:01
  • 浏览量:

requestAnimationFrame

requestAnimationFrame是一个浏览器的宏任务,它的用法与setTimeout很相似,只是不需要设置时间间隔。

requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行

requestAnimationFrame特点

【1】requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,如果系统绘制率是 60Hz,那么回调函数就会在16.7ms后执行一次,如果绘制频率是75Hz,那么这个间隔时间就变成了 1000/75=13.3ms。

换句话说就是,requestAnimationFrame的执行步伐紧跟着系统的绘制频率。它能保证回调函数在屏幕每一次的绘制间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。

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

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

跟setTimeout和setInterval的对比

setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔,参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行

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

IE9-浏览器不支持该方法,可以使用setTimeout来兼容

// 简单兼容
if (!window.requestAnimationFrame) {
    requestAnimationFrame = function(fn) {
        setTimeout(fn, 17);
    };    
}
123456

// 严格兼容 , 因为setTimeout内部运行也需要时间,以及需要给回调的第一个参数返回时间戳
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;
    }
}
12345678910111213

_requestAnimationFrame是一个浏览器的宏任务,它的用法与_requestAnimationFrame是一个浏览器的宏任务,它的用法与

应用场景1.监听 scroll 函数

页面滚动事件(scroll)的监听函数,就很适合用这个 api,推迟到下一次重新渲染。

$(window).on('scroll', function () {
  window.requestAnimationFrame(scrollHandler)
})

2.平滑滚动到页面顶部

const scrollToTop = () => { 
  const c = document.documentElement.scrollTop || document.body.scrollTop 
  if (c > 0) {  
    window.requestAnimationFrame(scrollToTop) 
    window.scrollTo(0, c - c / 8) 
  }
}
scrollToTop()

大量数据渲染

比如对十万条数据进行渲染,主要由以下几种方法:

1.使用定时器

// 需要插入的容器
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)

2.使用 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)

3.监控卡顿方法

每秒中计算一次网页的 FPS,获得一列数据,然后分析。通俗地解释就是,通过requestAnimationFrameAPI 来定时执行一些 JS 代码,如果浏览器卡顿,无法很好地保证渲染的频率,1s 中 frame 无法达到 60 帧,即可间接地反映浏览器的渲染帧率。

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)
}

我们可以定义一些边界值,比如连续出现 3 个低于 20 的 FPS 即可认为网页存在卡顿。