关于 requestAnimationFrame

377 阅读1分钟

动画

看名字我们就知道这个 API 和动画有关系。为了便于理解 requestAnimationFrame ,可以把它和 setTimeout 来做类比,假如要使用 setTimeout 来实现一个进度条的动画效果,那么代码可以像下面这样写:

<div class="box" style="width: 0;">0%</div>
.box { line-height: 24px; background: red; }
box.addEventListener('click', function() {
  let timer = setTimeout(function fn() {
    // 行内样式才可以通过 style.width 获取到
    box.style.width = parseInt(box.style.width) + 5 + 'px'
    box.innerText = parseInt(box.style.width) / 2 + '%'
    timer = setTimeout(fn, 17)
    if (parseInt(box.style.width) >= 200) {
      clearTimeout(timer)
    }
  }, 17)
})

我们可以使用 requestAnimationFrame 来改写,其区别主要在于不需要提供延迟参数了,浏览器自身会根据自己的渲染机制来调用此函数。

代码如下:

box.addEventListener('click', function() {
  let timer = requestAnimationFrame(function fn() {
    box.style.width = parseInt(box.style.width) + 5 + 'px'
    box.innerText = parseInt(box.style.width) / 2 + '%'
    timer = requestAnimationFrame(fn)
    if (parseInt(box.style.width) >= 200) {
      cancelAnimationFrame(timer)
    }
  })
})

关于回调的参数

使用 requestAnimationFrame 时,接收一个回调,回调的参数是一个毫秒值,表示下次重绘的时间。

MDN 上的范例很好地解释了该如何灵活使用该参数:

const element = document.getElementById('some-element-you-want-to-animate');
let start;

function step(timestamp) {
  if (start === undefined)
    start = timestamp;
  const elapsed = timestamp - start;

  //这里使用`Math.min()`确保元素刚好停在200px的位置。
  element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';

  if (elapsed < 2000) { // 在两秒后停止动画
    window.requestAnimationFrame(step);
  }
}

window.requestAnimationFrame(step);

参考链接

  1. requestAnimationFrame 到底解决的是什么问题?
  2. 张鑫旭 requestAnimationFrame
  3. MDN requestAnimationFrame
  4. 《JavaScript高级程序设计》第四版 第18章 动画与 Canvas 图形