requestAnimationFrame本身不提供缓动逻辑,需结合缓动函数与时间进度驱动属性变化:记录开始时间,每帧计算归一化进度t,经缓动函数处理后更新样式;推荐用performance.now()、确保终值精确、避免触发布局、统一调度多动画。

requestAnimationFrame 本身不提供缓动逻辑,它只是按浏览器刷新节奏调度动画帧;要实现缓动效果,需要手动将缓动函数(如 ease-in、ease-out)与时间进度结合,驱动目标属性变化。
核心思路:用时间差计算缓动进度
缓动动画的关键是把“总持续时间”和“已过去时间”映射为一个 [0,1] 区间的进度值,再用缓动函数加工这个值,最后应用到属性上。
- 记录动画开始时间(
startTime = performance.now()) - 在每一帧中计算已过时间:
elapsed = performance.now() - startTime - 算出归一化进度:
t = Math.min(elapsed / duration, 1)(防止超时) - 代入缓动函数得到实际插值比例:
easedT = easeOutCubic(t) - 用该比例更新目标值,例如:
element.style.transform = `translateX(${easedT * 200}px)`
常用缓动函数示例(可直接复用)
这些函数接收 t ∈ [0,1],返回处理后的 easedT ∈ [0,1]:
-
linear:
t -
easeInQuad:
t * t -
easeOutCubic:
1 - Math.pow(1 - t, 3) -
easeInOutSine:
0.5 * (1 - Math.cos(t * Math.PI))
建议封装成独立函数,便于组合和调试。
实现 Markdown↔ HTML 互转,支持导出 PDF,用于用户请求的文档格式转换。
立即学习“前端免费学习笔记(深入)”;
完整实现结构(带启停控制)
避免用递归调用 rAF,推荐使用循环 + 标志位管理状态:
- 定义
let animationId = null和let isRunning = false - 启动时记录
startTime,调用animate() -
animate()内部:计算t→ 应用缓动 → 更新样式 → 若t 则 <code>animationId = requestAnimationFrame(animate) - 停止时调用
cancelAnimationFrame(animationId)并重置状态
注意事项与优化点
实际使用中容易忽略但影响体验的细节:
- 用
performance.now()而非Date.now(),精度更高、不受系统时间调整影响 - 动画结束时确保属性精确到达终值(比如补一句
if (t >= 1) { updateToFinal(); }),避免因浮点误差残留 - 对 transform/opacity 等属性做动画,优先触发 GPU 加速,避免 layout 触发(如 top/left/width)
- 多个动画共存时,可统一用单个 rAF 调度器管理,减少帧率竞争


















