JavaScript动画本质是requestAnimationFrame驱动的定时重绘,通过修改transform/opacity等不触发重排的属性实现高性能动画,需注意取消机制、时间控制精度及与滚动等事件的协同调度。

JavaScript 动画靠的是定时重绘,不是“播放”
浏览器没有内置的“动画轨道”或“时间轴”,所谓 JavaScript 动画,本质是反复修改元素的样式(比如 transform、opacity),并在每一帧触发重排/重绘。关键在于控制节奏和时机——用 requestAnimationFrame 替代 setTimeout 或 setInterval,才能匹配屏幕刷新率(通常是 60fps),避免掉帧或卡顿。
-
requestAnimationFrame会把回调安排在下一帧绘制前执行,系统自动节流,比手动算毫秒更可靠 - 直接操作
style.left/top触发 layout,性能差;优先用transform和opacity,它们走合成层,不触发重排 - 动画中途取消必须调用
cancelAnimationFrame,否则回调持续占用资源
用 requestAnimationFrame 写一个平滑移动效果
下面是一个从左到右移动 div 的最小可行示例,不依赖任何库:
const el = document.querySelector('#box');
let x = 0;
const targetX = 400;
const speed = 2; // 像素/帧
function animate() {
if (x < targetX) {
x += speed;
el.style.transform = translateX(${x}px);
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
注意:speed 是每帧位移量,不是每秒像素;实际速度取决于帧率。若要实现“真实时间控制”(如 2 秒内完成),需记录起始时间,用 performance.now() 计算已过去毫秒数,再插值计算当前位置。
CSS 动画和 JS 动画该选哪个?
不是非此即彼,而是看控制粒度和交互需求:
立即学习“Java免费学习笔记(深入)”;
- 纯展示型动效(加载旋转、悬停缩放)→ 用
@keyframes+animation,浏览器优化充分,功耗低 - 需要响应用户输入(拖拽中实时反馈)、动态计算终点、或逐帧干预(比如物理模拟)→ 必须用 JS +
requestAnimationFrame - 混合使用常见:CSS 定义基础过渡(
transition: transform 0.3s),JS 控制是否添加 class 触发动画;但要注意,JS 修改transform后立即读取offsetLeft可能强制同步布局,造成卡顿
容易被忽略的细节:动画帧与设备像素比、滚动绑定
在高 DPR 屏幕(如 MacBook Retina、安卓旗舰)上,如果动画涉及 canvas 绘图或自定义路径,需用 window.devicePixelRatio 缩放画布尺寸,否则线条模糊;而绑定 scroll 事件做视差动画时,直接在事件回调里改样式必然卡顿——必须节流 + requestAnimationFrame 中央调度:
let isQueued = false;
window.addEventListener('scroll', () => {
if (!isQueued) {
isQueued = true;
requestAnimationFrame(() => {
const y = window.scrollY;
document.body.style.backgroundPositionY = `${y * 0.5}px`;
isQueued = false;
});
}
});真正难的不是让东西动起来,而是动得准、省电、不抢主线程、且在各种设备上表现一致。很多“动画卡顿”问题,根源不在动画本身,而在它和滚动、输入、数据请求之间的资源争抢。










