requestAnimationFrame 是浏览器提供的用于实现平滑动画的 API。它会在浏览器下一次重绘之前调用指定的回调函数,从而保证动画与屏幕刷新率同步,避免不必要的性能消耗。
下面给出两个最简单的实用案例,帮助你快速上手。
案例一:方块左右来回移动(基础循环动画)
这个例子让一个蓝色方块在页面中从左向右移动,碰到右边界后反向,碰到左边界再折返,形成来回摆动的效果。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>requestAnimationFrame 基础移动</title>
<style>
#box {
width: 80px;
height: 80px;
background: #3498db;
border-radius: 8px;
position: absolute;
top: 100px;
left: 0;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
const box = document.getElementById('box');
let x = 0; // 当前水平位置
let direction = 1; // 1 向右, -1 向左
const speed = 3; // 每帧移动像素数
function animate() {
// 更新位置
x += speed * direction;
// 边界检测(假设视口宽度为窗口宽度)
const maxX = window.innerWidth - box.offsetWidth;
if (x >= maxX || x <= 0) {
direction *= -1; // 反向
}
// 应用位置
box.style.left = x + 'px';
// 继续下一帧
requestAnimationFrame(animate);
}
// 启动动画
requestAnimationFrame(animate);
</script>
</body>
</html>
要点:
- 每帧直接更新
left属性,配合position: absolute。 - 使用
direction变量控制运动方向,碰到边界即反转。 - 循环调用
requestAnimationFrame(animate)形成无限动画。
案例二:进度条 0→100% 匀速填充(基于时间戳控制)
这个例子展示如何利用 requestAnimationFrame 回调中的时间戳(DOMHighResTimeStamp)来控制动画进度,而不是依赖固定的帧率。进度条在 3 秒内从 0% 填充到 100%。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>requestAnimationFrame 进度条</title>
<style>
#progress-container {
width: 400px;
height: 30px;
background: #ecf0f1;
border-radius: 15px;
overflow: hidden;
margin: 50px auto;
}
#progress-bar {
width: 0%;
height: 100%;
background: #e67e22;
border-radius: 15px;
transition: none; /* 我们手动控制,不用CSS过渡 */
}
#info {
text-align: center;
font-family: Arial, sans-serif;
font-size: 18px;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="progress-container">
<div id="progress-bar"></div>
</div>
<div id="info">0%</div>
<script>
const bar = document.getElementById('progress-bar');
const info = document.getElementById('info');
const duration = 3000; // 持续 3 秒
let startTime = null; // 记录动画开始的时间戳
function updateProgress(timestamp) {
// 第一次调用时记录起始时间
if (startTime === null) {
startTime = timestamp;
}
// 计算已经过去的时间(毫秒)
const elapsed = timestamp - startTime;
// 计算进度(0~1),并限制最大为 1
const progress = Math.min(elapsed / duration, 1);
// 更新进度条宽度和文字
const percent = progress * 100;
bar.style.width = percent + '%';
info.textContent = Math.round(percent) + '%';
// 如果未完成,继续下一帧
if (progress < 1) {
requestAnimationFrame(updateProgress);
}
}
// 启动动画
requestAnimationFrame(updateProgress);
</script>
</body>
</html>
要点:
- 回调函数的第一个参数
timestamp是浏览器提供的精确时间戳(毫秒)。 - 通过计算
elapsed / duration得到 0~1 的进度值,保证动画在固定时间内完成,不受帧率波动影响。 - 当进度达到 1 时停止循环,避免不必要的性能开销。
这两个案例覆盖了 基于帧的连续移动 和 基于时间的可控动画 ,是 requestAnimationFrame 最常用的两种模式。你可以在此基础上扩展更复杂的动画效果。