animation-fill-mode: forwards 必须加,否则动画结束 width 会回退至 0%;需显式定义 keyframes 起止帧,用 css 变量 --progress 配合 calc() 或 js 动态设 width 并触发 reflow 确保动画生效。

animation-fill-mode: forwards 必须加,否则进度条动完就回退
CSS 动画默认执行完会恢复初始状态,width 从 0% 动到 60% 后,若不锁定终点,立刻跳回 0%。这不是“没动”,是动完了又撤了。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 动画定义里必须写
animation-fill-mode: forwards - 不能只靠
animation-direction: normal或animation-iteration-count: 1替代 - 如果用
@keyframes控制width,起始帧(0%)和结束帧(100%)都要显式写清楚,别依赖默认值
@keyframes grow {
0% { width: 0%; }
100% { width: 60%; }
}
.bar {
animation: grow 1.2s ease-out;
animation-fill-mode: forwards; /* 关键 */
}百分比数值不能写死在 keyframes 里,得用内联 style 或 CSS 变量传入
你不可能为每个进度值(比如 37%、82%、95%)都写一个 @keyframes。硬编码会失去动态性,也违背“根据数值控制”的需求。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 把进度值抽成 CSS 自定义属性,比如
--progress: 60(注意单位是纯数字,不带 %) - 在
@keyframes中用calc()转换:width: calc(var(--progress) * 1%); - 或更稳妥的做法:直接用 JS 设置元素的
style.width,再触发动画(见下一条) - 不要在
@keyframes里写width: var(--progress)—— 大部分浏览器不支持变量插值进 keyframes
JS 设置 width 后立即触发动画,得用 reflow 强制重绘
直接 el.style.width = '60%'; 不一定会触发 CSS 过渡/动画,尤其当元素刚插入 DOM 或之前 width 是 auto 时,浏览器可能合并样式计算,跳过动画。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 设置
width后,立刻读一个布局相关属性(如offsetHeight),强制同步重排(reflow) - 确保元素已有动画样式(
transition或animation)且未被display: none或opacity: 0隐藏 - 避免在 Vue/React 的批量更新中连续改
width,可能被合并;需要动画时,用nextTick或requestAnimationFrame包一层
el.style.width = '60%';
el.offsetHeight; // 触发 reflow,让动画真正开始
// 或
requestAnimationFrame(() => {
el.style.width = '60%';
});IE 和旧版 Safari 不支持 CSS 自定义属性 + calc() 动画,降级方案要预留
如果你的项目还要兼容 IE11 或 iOS var(--progress) 在 @keyframes 中基本无效,calc() 也不能动态响应变量变化。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 优先用 JS 控制
width+transition(比animation更可控、兼容更好) - 给
.bar加transition: width 0.4s ease-out,然后只改style.width,不用 keyframes - 用
@supports (animation: test) and (color: color(display-p3 1 0 0))做特性检测,区分现代/旧浏览器逻辑 - 别指望
animation-timing-function在所有浏览器里表现一致,ease-out在 Safari 上有时偏急刹,可微调为cubic-bezier(0.2, 0.8, 0.3, 1)
动画终点位置是否生效,取决于你有没有真正让浏览器“看到”那个终值——不是写了 100%,而是它得算出来、渲染出来、且不被后续样式覆盖。这点最容易被当成“动画没跑”,其实是卡在样式链或渲染时机上。










