动画循环播了五遍,进度条卡在 20% 就不动了——不是你的代码有误,是浏览器从来没打算告诉你总进度
动画循环播了五遍,进度条卡在 20% 就不动了——不是你的代码有误,是浏览器从来没打算告诉你总进度
你写了一个加载动画,循环播放 5 次,每次旋转一圈。进度条绑在 animation.currentTime / animation.duration 上——理论上应该从 0% 跑到 100%,然后重置,再跑下一遍。
但实际上,进度条跑到 20% 就停了。刷新、再跑,还是 20%。这不是你的代码有 bug——这是 currentTime / duration 天然只反映当前这一次迭代的进度,从来没打算告诉你整个动画跑了多少。
Safari 26.2 今天把这件事用一个属性彻底说清楚了。
旧方法为什么不工作
Web Animations API 的 currentTime 和 duration 是一对搭档:
const progress = animation.currentTime / animation.duration;
对于单次播放的动画,这个值确实代表总进度。但对于循环动画,这只是当前这一次迭代的进度。5 次迭代的动画,跑到第 2 遍的第 5% 位置,currentTime / duration 永远是 0.05,而不是整体的 0.21(第 2 遍的第 5%,相当于整体 21%)。
这个差异在时间驱动的动画里还算可控——你心里知道是第几遍,再做个乘法。但到了 scroll-driven 动画(滚动触发的动画),timeline 根本不是时间,currentTime 表达的是滚动进度在时间线上的映射,不同用户滚动速度不同,算出来的”百分比”完全无法横向比较。
overallProgress:所有迭代的总进度
Animation.overallProgress 是 Web Animations Module Level 2 规范的属性,返回 0 到 1 之间的数字,代表动画所有迭代的总进度。它在所有类型的 timeline 下行为一致:
const progress = animation.overallProgress; // 0.0 ~ 1.0
对于 5 次迭代的动画:
- 第 1 遍开始:
overallProgress = 0 - 第 2 遍开始:
overallProgress = 0.2 - 第 3 遍中间:
overallProgress = 0.5(第 3 遍的一半) - 全部播完:
overallProgress = 1.0
关键:无论动画循环几次、timeline 是什么类型,0 就是开始,1 就是结束,中间的值就是总进度。
真实场景:进度条
这在加载动画里最直观。一个 5 次旋转的加载动画,进度条要跟着显示”第几遍”:
// 旧方法:卡在 20%(每次重置)
progressBar.style.width = (animation.currentTime / animation.duration * 100) + '%';
// 新方法:真正显示总进度
function updateProgress() {
if (animation.playState !== 'finished') {
const pct = Math.floor(animation.overallProgress * 100);
progressBar.style.width = pct + '%';
progressBar.textContent = `加载中 ${pct}%`;
requestAnimationFrame(updateProgress);
} else {
progressBar.textContent = '完成';
}
}
overallProgress 等于 1 时,整个动画序列全部播完,进度条正好走到 100%。不会出现”第 2 遍卡在 20%”的情况。
滚动驱动动画:更可靠的同步
scroll-driven 动画(scroll() / view() timeline)的进度计算比时间驱动更复杂——不同用户滚动速度不同,同一段滚动 content-progress 的值不一样。但在 overallProgress 里,0.5 就是整体完成了一半,与用户滚动多快无关:
const viewTimeline = new ViewTimeline({ subject: section });
const animation = element.animate([
{ height: '0%' },
{ height: '100%' }
], {
timeline: viewTimeline,
fill: 'forwards'
});
// 同步另一段文字进度——0.5 就是整个视差段落滚动了一半
animation.overallProgress; // 一致的 0-1,不受滚动速度影响
这在视差滚动场景里很有用:视差背景的动画进度和前景内容的进度需要精确对齐,overallProgress 提供了统一的参考值。
边界情况
overallProgress 有几个边界需要注意:
| 情况 | 返回值 |
|---|---|
| 动画没有 timeline / 未播放 / 处于非活跃状态 | null |
iterations 设置为 Infinity |
null |
currentTime 被设置为负值 |
0 |
duration 为 0 |
1 |
使用前建议做一下空检查:
const p = animation.overallProgress;
if (p !== null) {
progressBar.style.width = (p * 100) + '%';
}
下一步
Safari 26.2(STP 28)+ Chrome 133 + Firefox 133,三大引擎全部支持,Baseline 2025 已覆盖。Chrome DevTools 也支持在 Animation 面板里直接看到 overallProgress 的值。
如果你的页面有循环播放的动画,或者依赖动画进度做 UI 同步,可以把 currentTime / duration 替换成 overallProgress,一次替换,解决一类进度计算不一致的问题。
评论区
登录后可评论。