requestAnimationFrame
的原理
requestAnimationFrame
是浏览器提供的一个 API,用于更高效地实现动画。它的核心原理是让浏览器在下次重绘(repaint)之前执行指定的回调函数,从而确保动画的流畅性,并避免不必要的性能开销。
核心特点
- 节能高效:
- 只有在页面处于激活状态时,
requestAnimationFrame
才会运行,页面不可见时会暂停动画,从而节省系统资源。
- 只有在页面处于激活状态时,
- 与屏幕刷新同步:
- 回调函数的执行频率与显示器的刷新率(通常为60Hz,即每秒60帧)保持一致,避免了定时器(如
setTimeout
或setInterval
)可能导致的帧数不均匀或动画卡顿问题。
- 回调函数的执行频率与显示器的刷新率(通常为60Hz,即每秒60帧)保持一致,避免了定时器(如
使用方法
基本语法
let animationId = requestAnimationFrame(callback);
- 参数:
callback
:一个函数,在下一次重绘前会被执行。
- 返回值:
- 一个
ID
,可以通过cancelAnimationFrame
来取消。
- 一个
完整示例
let startTime = null;
function step(timestamp) {
if (!startTime) startTime = timestamp; // 初始化起始时间
const progress = timestamp - startTime; // 计算动画运行时间
// 在页面中移动一个元素
const element = document.getElementById("animatedBox");
element.style.transform = `translateX(${Math.min(progress / 10, 200)}px)`; // 限制最大位移
if (progress < 2000) { // 控制动画时长为 2000ms
requestAnimationFrame(step); // 继续下一帧动画
}
}
requestAnimationFrame(step);
取消动画
const animationId = requestAnimationFrame(callback);
// 取消动画
cancelAnimationFrame(animationId);
应用场景
1. 页面动画
- 创建基于帧的流畅动画,如滚动、移动、缩放等。
- 例如,制作平滑的滚动效果:
function smoothScroll(targetPosition, duration) {
const startPosition = window.scrollY;
const distance = targetPosition - startPosition;
let startTime = null;
function animation(currentTime) {
if (!startTime) startTime = currentTime;
const elapsedTime = currentTime - startTime;
const easeInOutQuad = (t, b, c, d) => {
t /= d / 2;
if (t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
};
const scrollPosition = easeInOutQuad(elapsedTime, startPosition, distance, duration);
window.scrollTo(0, scrollPosition);
if (elapsedTime < duration) {
requestAnimationFrame(animation);
}
}
requestAnimationFrame(animation);
}
// 使用:
smoothScroll(500, 1000); // 滚动到500px位置,用时1000ms
2. 游戏动画
- 在 HTML5 Canvas 或 WebGL 中,与游戏的帧刷新同步。
- 例如,一个简单的球移动动画:
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let x = 50, y = 50, dx = 2, dy = 2;
function drawBall() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2); // 画圆
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
function update() {
drawBall();
x += dx;
y += dy;
if (x + dx > canvas.width || x + dx < 0) dx = -dx;
if (y + dy > canvas.height || y + dy < 0) dy = -dy;
requestAnimationFrame(update);
}
update();
3. 数据可视化
- 动态更新图表、数据仪表盘等,如基于
D3.js
或Three.js
的场景中实现平滑更新。
4. 防止页面卡顿
- 适用于实现一些逐帧的操作,比如逐帧加载图像。
setInterval
和 requestAnimationFrame
的区别
特性 | setTimeout | requestAnimationFrame |
---|---|---|
频率控制 | 不保证与刷新率同步,可能导致丢帧 | 与刷新率一致,通常为 60FPS |
节能 | 页面隐藏时仍在运行,浪费资源 | 页面隐藏时自动暂停 |
使用复杂度 | 更简单,直接延时调用 | 更适合动画,需自行管理帧逻辑 |
性能优化 | 不够流畅,容易卡顿 | 更流畅,避免视觉卡顿 |
注意事项
- 节流控制:
- 避免在动画中进行过多计算或 DOM 操作,影响性能。
- 兼容性:
- 现代浏览器都支持,但可以使用 Polyfill 以兼容旧版本浏览器:
window.requestAnimationFrame = window.requestAnimationFrame || function(callback) { return setTimeout(callback, 1000 / 60); };
- 现代浏览器都支持,但可以使用 Polyfill 以兼容旧版本浏览器:
通过合理使用 requestAnimationFrame
,可以实现流畅、节能的动画效果,非常适合高性能要求的场景。