用requestAnimationFrame替代setTimeout可实现平滑动画,因其由浏览器统一调度绘制时机;需在回调末尾递归调用自身,避免耗时操作,并可用performance.now()控制时间差。

用 requestAnimationFrame 替代 setTimeout 实现平滑动画
直接用 setTimeout 控制样式变化容易掉帧,尤其在页面负载高时。浏览器无法协调绘制时机,导致卡顿或跳变。requestAnimationFrame 会把动画逻辑交给浏览器统一调度,保证每帧在下一次重绘前执行,是 Web 动画的底层推荐方式。
- 必须在回调函数末尾再次调用
requestAnimationFrame才能持续执行 - 不要在回调里做耗时操作(如 dom 查询、大量计算),否则会挤占渲染时间
- 可配合
performance.now()做时间差控制,实现精确的缓动逻辑
let startTime = null; function animate(timestamp) { if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; const progress = Math.min(elapsed / 1000, 1); // 持续1秒 element.style.transform = `translateX(${progress * 200}px)`; if (progress < 1) requestAnimationFrame(animate); } requestAnimationFrame(animate);
用 transition css 属性做声明式位移动画
对简单位移、缩放、透明度变化,纯 CSS 更轻量、更稳定,且支持硬件加速。关键不是写动画本身,而是控制触发时机和过渡参数。
- 必须提前在 CSS 中定义
transition,仅靠 js 改变style不会生效 - 避免同时过渡多个属性(如
left+top+opacity),优先用transform和opacity - 过渡时间单位用
ms更易控制,例如transition: transform 300ms ease-out
/* CSS */ .box { transition: transform 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94); } .box.active { transform: translateX(100px); } / JS / element.classlist.add('active');
用 animate() API 实现可控的关键帧动画
Element.animate() 是现代浏览器原生支持的 javaScript 动画接口,比手动管理 requestAnimationFrame 更简洁,且自带播放控制能力(暂停、反向、时间偏移)。
- 不兼容 IE,safari 16.4+ 才支持
iterationStart等高级参数 - 动画结束后默认不保留最终状态,需显式设置
fill: 'forwards' - 传入的 keyframes 对象中,
offset值必须在 0–1 之间,且有序
element.animate( [ { transform: 'scale(1)', opacity: 1 }, { transform: 'scale(1.2)', opacity: 0.7 } ], { duration: 400, fill: 'forwards', easing: 'ease-in-out' } );
为什么 left/top 动画性能差?
修改 left 或 top 会触发浏览器强制同步布局(layout),每次重排都要重新计算整个文档流,代价远高于只改 transform(只影响合成层)。
立即学习“Java免费学习笔记(深入)”;
- 即使元素已设
position: absolute,left变化仍会引发 layout -
transform: translateX()不影响文档流,且大多数情况下走 GPU 合成,帧率更稳 - 检查工具:chrome DevTools 的 Rendering 面板打开 “Paint flashing” 和 “FPS meter”,对比两种写法的闪烁区域和帧率波动
复杂动画真正难的不是“怎么动起来”,而是“怎么动得准、停得稳、不抢主线程”。比如缓动函数选错、未清理旧动画句柄、CSS 触发重排却不自知——这些细节比写第一行 requestAnimationFrame 更容易让效果失真。