用requestanimationframe配合贝塞尔曲线实现丝滑加载条:先用bezier-easing库生成缓动函数,再以raf驱动每帧计算归一化时间t并映射为缓动进度,结合两段式策略(模拟启动+真实加载)和transform硬件加速,避免重排与丢帧。

用 requestAnimationFrame 配合贝塞尔曲线实现丝滑加载条,核心是把「进度变化」交给缓动函数控制,再用 RAF 做高帧率驱动,避免 setInterval 的卡顿和丢帧问题。
用贝塞尔曲线定义缓动效果
贝塞尔曲线(如 cubic-bezier)本质是时间到进度的映射函数。CSS 中常用 cubic-bezier(0.25, 0.46, 0.45, 0.94) 这类值,但 JS 里不能直接调用——需手动实现或借助轻量函数库(如 bezier-easing)。
推荐做法:引入 bezier-easing(仅 1KB),传入四个控制点,得到一个可复用的缓动函数:
import bezierEasing from 'bezier-easing'; <p>// 模拟 CSS 的 ease-in-out:cubic-bezier(0.42, 0, 0.58, 1) const easeInOut = bezierEasing(0.42, 0, 0.58, 1);</p>
用 requestAnimationFrame 驱动动画帧
RAF 自动对齐屏幕刷新率(通常 60fps),且在页面不可见时自动暂停,比 setTimeout 或 setInterval 更精准节能。
关键逻辑:记录起始时间,每帧计算已过去的时间占比(t ∈ [0,1]),再用贝塞尔函数算出当前进度值:
let startTime = null;
const duration = 1200; // 动画总时长(ms)
const startProgress = 0;
const endProgress = 100;
<p>function animate(currentTime) {
if (!startTime) startTime = currentTime;
const elapsed = currentTime - startTime;
const t = Math.min(elapsed / duration, 1); // 归一化时间</p><p>// 贝塞尔缓动:t → 缓动后的进度比例
const easedT = easeInOut(t);
const currentProgress = startProgress + (endProgress - startProgress) * easedT;</p><p>// 更新 DOM(例如修改 width 或 transform)
progressBar.style.width = <code>${currentProgress}%</code>;</p><p>if (t </p><p>// 启动
requestAnimationFrame(animate);</p>适配加载过程:从 0% 到真实进度再到 100%
真实场景中,加载不是固定时长,而是异步事件驱动。建议采用「两段式」策略:
- 第一阶段:从 0% → 60%,用贝塞尔动画模拟“启动感”,不依赖真实数据(防白屏等待)
- 第二阶段:监听实际加载事件(如
XMLHttpRequest.progress或fetch的流式响应),动态更新目标进度;当接近完成时,再用贝塞尔补满到 100% - 全程只用一个 RAF 循环,通过状态机切换插值逻辑,避免频繁启停
优化细节提升丝滑感
光有 RAF 和贝塞尔还不够,这些细节决定是否真「丝滑」:
-
用 transform 或 opacity 触发硬件加速:改
width可能触发重排,建议用transform: scaleX()模拟拉伸效果 - 避免 layout thrashing:不要在动画帧内读取 offsetWidth、getBoundingClientRect 等会强制同步布局的属性
-
添加 cancel 支持:暴露
cancel()方法,便于加载失败或跳转时及时终止动画 -
降级兼容性:老旧浏览器可用
window.performance.now()替代currentTime参数,或 fallback 到setTimeout(但保留贝塞尔逻辑)
不复杂但容易忽略:贝塞尔控制的是「时间 vs 进度」的关系,而 RAF 提供的是「稳定的时间源」——两者结合,才能让加载条既符合设计节奏,又不卡顿掉帧。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











