requestAnimationFrame 定时器

49 阅读2分钟

一、概述

  • 概念:帧动画,就是可以一帧一帧的执行动画
  • 解决问题:解决了浏览器不知道javascript动画什么时候开始、不知道最佳循环间隔时间的问题。它是跟着浏览器的绘制走的,如果浏览器绘制间隔是16.7ms,它就按这个间隔绘制;如果浏览器绘制间隔是10ms, 它就按10ms绘制。这样就不会存在过度绘制的问题,动画不会丢帧

特点:

  1. 就算很多个requestAnimationFrame()要执行,浏览器只要通知一次就可以了。而setTimeout是多个独立绘制。
  2. 一旦页面不出于当前页面(比如:页面最小化了),页面是不会进行重绘的,自然requestAnimationFrame也不会触发(因为没有通知)。页面绘制全部停止,资源高效利用

二、用法

  • requestAnimationFrame是HTML5版本新增的API方法
  • 被绑定在window对象身上
  • 接收一个回调函数作为参数
  • 返回值是当前执行的唯一标志,用来清除这次执行(与计时器类似)

注意没有时间参数,因为会自动随着屏幕的刷新频率自动执行,所以最终语法为:


let myReq;
let i = 0;
function step(timestamp) {
    console.log(i++);
    myReq = window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);


window.cancelAnimationFrame(myReq);    // 清除方式


兼容性

1、requestAnimationFrame()的兼容性封装

由于mozRequestAnimationFrame()是HTML5的新功能,目前各大浏览器的支持情况各异。如果希望代码具备更好的跨平台性,可以考虑使用下面的代码实现各平台兼容性:

if(!window.requestAnimationFrame) {
 window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
 window.mozRequestAnimationFrame ||
 window.oRequestAnimationFrame ||
 window.msRequestAnimationFrame ||
 function(callback) {
  var self = this, start, finish;
  return window.setTimeout(function() {
   start = +new Date();
   callback(start);
   finish = +new Date();
   self.timeout = 1000/60 - (finish - start);
  }, self.timeout);
 });
}

3、cancelRequestAnimFrame()的兼容性封装:

W3C也提供了cancelRequestAnimationFrame()方法,用于取消回调函数。requestAnimationFrame()方法会返回一个对象,用做标识回掉函数身份。若要取消回调函数的执行,可将其传给cancelRequestAnimationFrame()

window.cancelRequestAnimFrame = ( function() {
 return window.cancelAnimationFrame ||
  window.webkitCancelRequestAnimationFrame ||
  window.mozCancelRequestAnimationFrame ||
  window.oCancelRequestAnimationFrame ||
  window.msCancelRequestAnimationFrame ||
  clearTimeout;
} )();

三、 通过requestAnimationFrame 来实现定时器

function setInterval(callback, interval) {
  let timer
  const now = Date.now
  let startTime = now()
  let endTime = startTime
  const loop = () => {
    timer = window.requestAnimationFrame(loop)
    endTime = now()
    if (endTime - startTime >= interval) {
      startTime = endTime = now()
      callback(timer)
    }
  }
  timer = window.requestAnimationFrame(loop)
  return timer
}
let a = 0
setInterval(timer => {
  console.log(1)
  a++
  if (a === 3) cancelAnimationFrame(timer)
}, 1000)