17370845950

如何在javascript中实现动画效果_CSS与requestAnimationFrame结合指南【教程】
requestAnimationFrame 是最流畅可控的动画方式,因其与屏幕刷新同步、自动适配不同刷新率、页面不可见时暂停且支持精确控制;应仅操作 transform 和 opacity 等合成属性,避免重排重绘,并慎用与 CSS transition 的混合。

直接用 requestAnimationFrame 配合 CSS 变换(transformopacity 等)是最流畅、最可控的动画实现方式,比 setTimeout 或 CSS @keyframes 更适合需要动态响应或逐帧逻辑的场景。

为什么不用 setTimeoutsetInterval 做动画

它们不与屏幕刷新同步,容易掉帧、卡顿,且无法自动适配不同设备的刷新率(如 60Hz / 90Hz / 120Hz)。requestAnimationFrame 会把回调塞进浏览器下一帧的绘制队列,天然对齐显示器刷新节奏。

  • 即使你设 setTimeout(..., 16),实际执行时间可能漂移,尤其在页面后台运行或 CPU 负载高时
  • requestAnimationFrame 在页面不可见时自动暂停,省电又合理
  • 它返回一个 frameId,可随时用 cancelAnimationFrame(frameId) 中止,控制力更强

如何用 requestAnimationFrame 驱动 CSS 动画

核心思路:不操作 style.left/top 这类触发重排的属性,只改 transformopacity,并用 JS 控制动画进度,再通过 element.style.transform 同步到 DOM。

let start = null;
const duration = 300; // 毫秒
const element = document.querySelector('.box');

function animate(timestamp) { if (!start) start = timestamp; const progress = Math.min((timestamp - start) / duration, 1);

// 使用 transform 替代 left/top element.style.transform = translateX(${progress * 200}px); element.style.opacity = 1 - progress;

if (progress < 1) { requestAnimationFrame(animate); } }

requestAnimationFrame(animate);

  • 务必用 transformopacity —— 它们由合成器线程处理,不触发 layout/paint,性能最好
  • 避免在动画帧里读取 offsetLeftgetBoundingClientRect() 等会强制同步布局的 API
  • 如果需要 easing,直接在 progress 上套函数,比如 cubic-bezier(0.25, 0.46, 0.45, 0.94) 对应的实现,而非依赖 CSS transition-timing-function

和 CSS transition 混用的风险

JS 修改 style.transform 的同时,元素还挂着 transition: transform 300ms?这会导致行为不可控:过渡可能被中断、叠加、甚至产生“回弹”。

  • 要么全 JS 控制(删掉所有 transition),要么全 CSS 控制(用 classList.add('animate') 触发 @keyframes
  • 若必须混合(例如初始状态靠 CSS,后续靠 JS),记得在 JS 动画开始前清除 transition:element.style.transition = 'none';,动画第一帧后再恢复
  • 注意 will-change: transform 可提前提示浏览器优化,但别滥用——它会常驻图层,增加内存开销

真正难的不是写几行 requestAnimationFrame,而是判断该不该用它:简单入场/退出动画

transition 更轻量;需要拖拽跟随、滚动视差、物理模拟,才值得上 JS 帧控。别为了“高级感”绕远路。