JavaScript动画本质是requestAnimationFrame驱动的定时重绘,通过修改transform/opacity等不触发重排的属性实现高性能动画,需注意取消机制、时间控制精度及与滚动等事件的协同调度。
浏览器没有内置的“动画轨道”或“时间轴”,所谓 JavaScript 动画,本质是反复修改元素的样式(比如 transform、opacity),并在每一帧触发重排/重绘。关键在于控制节奏和时机——用 requestAnimationFrame 替代 setTimeout 或 setInterval,才能匹配屏幕刷新率(通常是 60fps),避免掉帧或卡顿。
requestAnimationFrame 会把回调安排在下一帧绘制前执行,系统自动节流,比手动算毫秒更可靠style.left/top 触发 layout,性能差;优先用 transform 和 opacity,它们走合成层,不触发重排cancelAnimationFrame,否则回调持续占用资源下面是一个从左到右移动 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() 计算已过去毫秒数,再插值计算当前位置。
不是非此即彼,而是看控制粒度和交互需求:
立即学习“Java免费学习笔记(深入)”;
@keyframes + animation,浏览器优化充分,功耗低requestAnimationFrame
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;
});
}
});真正难的不是让东西动起来,而是动得准、省电、不抢主线程、且在各种设备上表现一致。很多“动画卡顿”问题,根源不在动画本身,而在它和滚动、输入、数据请求之间的资源争抢。