Skip to content

requestAnimationFrame

  • setTimeout 和 setInterval 都不精确,因为它们的内在运行机制是把动画代码添加到浏览器UI线程队列中以等待执行。
  • 如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行

window.requestAnimationFrame()

  • requestAnimationFrame() 最大的优势是由系统来决定回调函数的执行时机。
  • 如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,
  • requestAnimationFrame() 的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
  • requestAnimationFrame() 方法会告诉浏览器你希望执行一个动画。它要求浏览器在下一次重绘之前,调用用户提供的回调函数。

备注

若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用 requestAnimationFrame()requestAnimationFrame() 是一次性的。

危险

请确保总是使用第一个参数(或其他一些获取当前时间的方法)来计算动画在一帧中的进度,否则动画在高刷新率的屏幕中会运行得更快。

示例

完整代码
vue
<template>
  <!-- 测试requestAnimationFrame -->
  <div class="container">
    <div ref="box" class="box"></div>
    <button class="btn" @click="resetMove">🚀再次执行</button>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue';

const box = ref<HTMLDivElement | null>();

onMounted(() => {
  // 启动动画
  move();
});

const move = () => {
  // 到达界面停止
  if (box.value!.offsetLeft >= 200) return;
  // 移动
  box.value!.style.left = box.value!.offsetLeft + 1 + 'px';
  // requestAnimationFrame()是一次性的,必须重新调用,否则动画不会继续
  requestAnimationFrame(move);
};

const resetMove = () => {
  box.value!.style.left = '0px';
  move();
};
</script>

Released under the MIT License.