requestAnimationFrame()

160 阅读4分钟

一、requestAnimationFrame()请求动画帧:

1.requestAnimationFrame() 是一个 Web API,用于请求浏览器在下一次重绘之前调用指定的回调函数。
   当我们使用 requestAnimationFrame(callback) 来请求动画帧时,浏览器会在下一次重绘之前调用 callback 函数。这样可以确保在适当的时间执行动画或其他与页面刷新相关的操作,以获得更平滑和高性能的动画效果。

2.使用 requestAnimationFrame() 的优点:
  1).它会自动适应浏览器的刷新频率,并在需要的时候跳过某些帧,以节省资源并提供流畅的动画效果(会要求浏览器根据自己的频率进行一次重绘,它会接收一个回调函数作为参数,在浏览器即将开始重绘时,会调用这个函数,并会给这个函数传入调用回调函数时的时间作为参数。之后会反复不断地调用requestAnimationFrame,以达到动画效果)。
	2).它还会与浏览器的垂直同步(VSync)机制进行配合,确保动画的更新与屏幕的刷新同步。
 	3).把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率.
	4).在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流.
	5) .requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
 缺点:
   1).由于requestAnimationFrame目前还存在兼容性问题,而且不同的浏览器还需要带不同的前缀。因此需要通过降级的方式对requestAnimationFrame进行封装,根据不同浏览器的情况从高级特性往低进行回退。
===>IE9-浏览器不支持该方法,可以使用setTimeout来兼容
  // 简单兼容
        if (!window.requestAnimationFrame) {
            requestAnimationFrame = function(fn) {
                            setTimeout(fn, 17);
            };    
        }
        // 严格兼容 , 因为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;
            }
        }

   2.requestAnimationFrame是在主线程上完成。这意味着,如果主线程非常繁忙,requestAnimationFrame的动画效果会大打折扣。
3.以下是一个使用 requestAnimationFrame() 的示例代码:
	function animate() {
                // 执行动画逻辑或其他操作
                requestAnimationFrame(animate); // 请求下一帧动画
	}
	requestAnimationFrame(animate); // 启动动画

	在上面的示例中,animate 函数是回调函数,用于执行动画逻辑。通过在 animate 函数内部再次调用 requestAnimationFrame(animate),实现了连续不断地请求下一帧动画,并使动画保持顺畅运行。

 需要注意的是,为了避免在不需要时浪费资源,应在动画完成或页面不再需要动画时停止调用 requestAnimationFrame()。可以使用条件控制语句或其他逻辑来控制动画的开始和停止。

二、应用场景

1、监听 scroll 函数

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

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

平滑滚动到页面顶部

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

2、大量数据渲染


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

(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,获得一列数据,然后分析。通俗地解释就是,通过 requestAnimationFrame API 来定时执行一些 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)
}