在现代Web开发中,实现动画效果的方式多种多样。除了传统的 setTimeout
和 setInterval
方法外,CSS3 的 transition
和 animation
也提供了强大的动画支持。然而,HTML5 引入了一个专门用于优化动画性能的 API ------ requestAnimationFrame
(简称 rAF),它能够帮助我们创建更加平滑、高效的动画体验。
本文将详细介绍 requestAnimationFrame
的工作原理、优势以及如何使用它来替代传统的定时器方法实现更佳的动画效果。
📌 一、基本概念与语法
✅ 定义:
window.requestAnimationFrame()
是一个用于请求浏览器在下一次重绘之前调用指定回调函数的方法。这意味着我们可以利用这个API来实现基于帧率的动画,而不需要担心定时器的延迟或丢帧问题。
✅ 语法:
javascript
let animationId = window.requestAnimationFrame(callback);
callback
:你希望在下次重绘之前执行的函数,该函数接收一个参数DOMHighResTimeStamp
,表示当前回调函数被触发的时间戳。- 返回值
animationId
:可以用来取消本次动画请求。
✅ 取消动画:
使用 cancelAnimationFrame(animationId)
来取消一个已经启动的动画。
🧩 二、requestAnimationFrame
vs setTimeout/setInterval
1. setTimeout/setInterval
实现动画的问题
❌ 缺点:
- 卡顿与抖动:由于定时器并非精确地按照设定时间间隔执行,尤其是在页面负载较高的情况下,容易导致动画不流畅。
- 资源浪费:即使页面不可见(如最小化窗口),定时器仍然会继续运行,消耗不必要的CPU资源。
- 丢帧现象:显示器通常以固定的刷新率(如60Hz)更新屏幕内容,而定时器的执行频率可能与其不同步,导致动画过程中出现丢帧。
示例代码:
javascript
function animate() {
// 动画逻辑
setTimeout(animate, 1000 / 60); // 尝试每秒60帧
}
animate();
2. requestAnimationFrame
的优势
✅ CPU节能:
当页面处于后台或不可见状态时,requestAnimationFrame
会自动暂停执行,避免了不必要的计算和渲染,从而节省了宝贵的CPU资源。
✅ 函数节流:
对于高频率事件(如 resize
, scroll
),requestAnimationFrame
能确保每个刷新周期内只执行一次回调,防止过多无效的重复操作。
✅ 集中DOM操作:
requestAnimationFrame
可以批量处理DOM操作,在每次重绘前一次性完成所有必要的DOM更新,减少了多次重绘或回流带来的性能开销。
💡 三、实战应用案例
1. 基本动画示例
下面是一个简单的移动方块的例子,展示了如何使用 requestAnimationFrame
创建动画:
javascript
let start = null;
const element = document.getElementById('animated');
element.style.position = 'absolute';
function step(timestamp) {
if (!start) start = timestamp;
const progress = timestamp - start;
element.style.left = Math.min(progress / 10, 200) + 'px';
if (progress < 2000) { // 继续动画直到2秒
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
2. 结合其他功能
a. 监听窗口大小变化
javascript
function resizeHandler() {
requestAnimationFrame(() => {
console.log('Window resized and processed during next repaint.');
});
}
window.addEventListener('resize', resizeHandler);
b. 滚动事件优化
javascript
let lastScrollTime = 0;
function scrollHandler() {
let now = performance.now();
if (now - lastScrollTime > 100) { // 控制滚动事件触发频率
requestAnimationFrame(() => {
console.log('Processing scroll event...');
lastScrollTime = now;
});
}
}
window.addEventListener('scroll', scrollHandler);
🧠 四、兼容性处理
尽管大多数现代浏览器都支持 requestAnimationFrame
,但在某些旧版浏览器中可能需要添加 polyfill:
javascript
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback) {
return window.setTimeout(callback, 1000 / 60);
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
📈 五、总结
对比维度 | setTimeout/setInterval |
requestAnimationFrame |
---|---|---|
精确度 | 不固定,依赖于系统调度 | 与屏幕刷新率同步 |
性能 | 可能导致频繁重绘/回流 | 批量处理DOM操作,减少重绘次数 |
资源管理 | 页面不可见时仍持续运行 | 自动暂停,节省CPU |
兼容性 | 广泛支持 | 需要Polyfill处理旧版浏览器 |