从卡顿到丝滑:揭秘 requestAnimationFrame 的魔力
你有没有遇到过这样的场景:页面上的动画看起来不流畅,画面有时一跳一跳,甚至在你点击或滚动时也感觉迟钝。别担心,这不全是你的代码问题,而是你还没找到实现完美动画的秘密武器!今天,我们就来揭开 requestAnimationFrame 这位动画背后的“幕后英雄”的神秘面纱,让你的页面动画从卡顿到丝滑如初。
动画为何总是不流畅?
在你手动编写动画时,如果只依赖传统的 setTimeout
或 setInterval
,你会发现动画效果时好时坏,时常会出现掉帧、卡顿的情况。这是因为这些定时器与浏览器的刷新频率并不同步,导致每一帧的执行时间不稳定。
而 requestAnimationFrame
是专为解决这一问题而生的,它能确保动画的每一帧与屏幕的刷新频率保持一致,从而实现流畅、自然的动画效果。接下来,让我们一起看看它究竟是如何工作的。
requestAnimationFrame 是什么?
requestAnimationFrame
是浏览器提供的一个 API,用于在浏览器的下一个重绘之前执行动画回调函数。与传统的 setTimeout
和 setInterval
不同,它与屏幕的刷新周期紧密同步,确保每秒有 60 帧的画面更新,从而大大提升动画的流畅性。
工作原理:
- 每次调用
requestAnimationFrame(callback)
,浏览器会在下一次重绘前调用该回调。 - 浏览器会自动计算每帧的执行时间,确保动画帧率的稳定性。
- 页面不可见时,动画会自动暂停,节省资源。
动画实现:从基础到复杂
1. 让盒子动起来:简单的平移动画
const box = document.getElementById('box');
let startTime: number | null = null;
function moveBox(timestamp: number) {
if (!startTime) startTime = timestamp;
const progress = (timestamp - startTime) / 1000; // 1秒内完成
box!.style.transform = `translateX(${Math.min(progress * 300, 300)}px)`;
if (progress < 3) {
requestAnimationFrame(moveBox);
}
}
requestAnimationFrame(moveBox);
解释:通过 timestamp
(时间戳)计算元素的运动进度,确保动画每秒有约 60 帧的更新,让动画既流畅又稳定。
2. 让运动更真实:缓动动画
线性动画虽然流畅,但缺乏自然感。使用缓动函数,你可以模拟加速或减速的效果,让动画看起来更符合物理规律。
function easeOutQuad(t: number): number {
return t * (2 - t); // 快到慢的缓动效果
}
function easingMove(timestamp: number) {
if (!startTime) startTime = timestamp;
const progress = Math.min(easeOutQuad((timestamp - startTime) / 1000), 1);
box!.style.transform = `translateX(${progress * 300}px)`;
if (progress < 1) {
requestAnimationFrame(easingMove);
}
}
requestAnimationFrame(easingMove);
解释:easeOutQuad
让动画从快到慢的平滑过渡,这种效果在现实世界中更常见,如物体停止时的惯性。
3. 协同运动:多元素动画
在更复杂的场景中,你可能需要多个元素协同工作,例如小球沿抛物线运动。
const ball = document.getElementById('ball');
let startTime: number | null = null;
function parabolaMove(timestamp: number) {
if (!startTime) startTime = timestamp;
const t = (timestamp - startTime) / 1000;
const x = t * 200; // 水平匀速移动
const y = 100 - (t * t * 200); // 垂直方向抛物线运动
ball!.style.transform = `translate(${x}px, ${y}px)`;
if (t < 2) {
requestAnimationFrame(parabolaMove);
}
}
requestAnimationFrame(parabolaMove);
解释:通过数学公式计算小球的抛物线轨迹,模拟更复杂的物理运动效果,适用于游戏或动画场景。
深度优化:让动画更流畅、更省资源
- 利用 GPU 加速:通过
transform
和opacity
等 CSS 属性可以让浏览器利用 GPU 渲染,从而实现更流畅的动画。 - 避免阻塞主线程:确保动画计算尽量简单,复杂的数学计算可以预先缓存。
- 资源管理:动画结束后,务必清理动画的回调函数,避免不必要的资源浪费。
总结:控制动画的每一帧
requestAnimationFrame
不仅仅是一个 API,它是 Web 动画开发的灵魂。它解决了传统动画方法的帧率不稳定、资源浪费等问题,能让你的动画如丝般顺滑。如果你还在使用 setInterval
来做动画,或许是时候尝试一下这个强大的工具了——让动画飞起来,向流畅的 Web 动画世界迈进!