纯css无法自动递增animation-delay,必须用js注入--i变量再通过calc(var(--i)*0.15s)实现延迟;需配合animation-fill-mode:forwards防止回退,并注意overflow:hidden与初始transform配合避免阶梯塌陷。

直接用 CSS 变量 + calc() 配合 :nth-child() 就能动态生成梯次延迟,比手写 10 个 :nth-child(n) 稳定且可维护——但变量必须由 JS 注入,纯 CSS 无法在运行时读取元素数量。
为什么不能只靠 :nth-child() + 预设数字
手写 .item:nth-child(1) { animation-delay: 0s; } 到 nth-child(20) 极易漏项或索引错位;更麻烦的是,一旦后端返回的列表长度变化(比如从 8 条变成 12 条),多出的项没延迟规则,就卡在初始态不动。SCSS 的 @for 编译时固定死数量,同样不适用动态列表。
--i 变量必须由 JS 注入才能生效
CSS 自身没有“遍历 DOM”的能力,var(--i) 的值只能靠外部写入。常见做法是:
- 在 JS 中遍历所有
.item,给每个元素设置style.setProperty('--i', index + 1) - CSS 中统一写
animation-delay: calc(var(--i, 1) * 0.15s); - 注意:
var(--i, 1)的 fallback 值很重要,否则未注入变量的元素会因calc(var(--i) * 0.15s)计算失败而丢掉整个animation-delay
animation-fill-mode: forwards 不可省略
动画播完后若不加 forwards,元素会瞬间回退到 opacity: 0; transform: translateY(20px) 状态,导致阶梯感断裂。尤其当列表项有 hover 效果或点击反馈时,这个回退会直接干扰交互。
- 必须和
animation声明写在同一行或同级规则里,单独写animation-fill-mode容易被覆盖 - 别用
backwards替代 —— 它只控制动画开始前的状态,对结束态无效 - 如果动画含位移(如
translateY),forwards还能避免布局重排抖动
移动端要注意 ease-in 的兼容性降级
部分安卓 WebView 对 cubic-bezier(0.2, 0.8, 0.4, 1) 渲染卡顿,表现为末尾几项动画突然跳变。稳妥做法是:
- 主逻辑用
ease-in - 加媒体查询兜底:
@media (prefers-reduced-motion: reduce) { .item { animation-timing-function: step-start; } } - 总延迟时长别超过单次动画时长的 1.2 倍,比如动画持续
0.4s,10 项最大延迟应 ≤0.48s,否则最后几项几乎看不出动画过程
真正容易被忽略的是容器的 overflow: hidden 和每项的初始 transform: translateY(20px) 必须同时存在——缺一不可。否则即使延迟算得再准,第一项还没动完,第二项就已经挤进视口,阶梯就塌成了一片。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











