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>