用 animation-delay 配合 :nth-child() 是最直接可行的方案,即通过为每个 li 手动设置递增延迟值(如 0s、0.1s、0.2s…)实现分段式进入动画,需配合 opacity: 0 和 transform 初始状态,并注意性能优化。

用 animation-delay 配合 :nth-child() 是最直接可行的方案
分段式进入动画本质是让列表项按顺序逐个触发,而不是同时开始。CSS 本身不支持“自动递增延迟”,但可以通过选择器手动为每个 <li> 设置不同延迟值实现。比如 5 个元素,延迟分别是 0s、0.1s、0.2s……这种写法兼容性好(IE10+)、无需 JS、逻辑清晰。
常见错误是直接给整个 ul 加动画,结果所有子项同步动;或者误以为 animation: fade-in 0.3s 自带 stagger 效果——它没有。
- 推荐用
:nth-child(n)批量控制:例如li:nth-child(1) { animation-delay: 0s; }、li:nth-child(2) { animation-delay: 0.1s; } - 如果列表长度不确定(比如动态渲染),纯 CSS 就难覆盖全,此时需 JS 动态加 class 或内联
style - 延迟值建议从
0.05s起步,太小人眼难分辨,太大又显得拖沓
用 @keyframes 定义入场动画时,别漏掉 opacity 和 transform 的初始状态
只写 transform: translateY(20px) 到 translateY(0) 不够——元素在动画前仍占据原位置,可能遮挡或影响布局。必须配合 opacity: 0 → opacity: 1,并确保初始状态就是隐藏的。
典型错误现象:页面加载瞬间看到所有列表项闪一下再动,就是因为没设 opacity: 0 作为默认样式。
- 基础动画定义示例:
@keyframes slide-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } - 必须提前给
li设opacity: 0,否则动画第一帧会从opacity: 1开始跳变 - 避免用
visibility: hidden替代opacity: 0,因为前者无法参与动画过渡
用 will-change: transform, opacity 可缓解快速连续触发动画的卡顿
当用户滚动进列表区域、或频繁切换 tab 触发重绘时,多个元素同时运行动画容易掉帧,尤其在中低端设备上。加 will-change 能提示浏览器提前升层,把动画交给 GPU 处理。
但别滥用:它会增加内存开销,且对静态元素反而有害。只应在明确需要动画的元素上设置。
- 推荐写法:
li { will-change: transform, opacity; }(放在通用规则里,而非只在动画 keyframes 中) - 如果列表项很多(>20 个),建议配合 Intersection Observer 延迟加载动画,避免一上来全量触发
- 注意 Safari 对
will-change的兼容性略差,可加-webkit-will-change前缀保底
JS 驱动的 stagger 更灵活,但要注意 getBoundingClientRect() 触发重排的风险
当需要根据滚动位置、视口比例或元素实际尺寸动态计算延迟(比如越靠下的元素延迟越长),CSS 静态选择器就不够用了,得用 JS。但高频读取布局信息极易引发强制同步重排(forced reflow)。
常见错误是循环里反复调用 el.getBoundingClientRect(),尤其在 scroll 事件中——这会让滚动变得卡顿。
- 正确做法:用
IntersectionObserver监听进入视口,再批量计算延迟,避免实时读取 - 延迟值可用
index * baseDelay + offset * distanceFromTop这类公式,比纯 CSS 更可控 - 若必须用
getBoundingClientRect(),请缓存结果,或用requestAnimationFrame节流
CSS 实现 stagger 的核心就两点:手动延迟 + 正确的初始状态。复杂交互场景下,JS 补位是必要的,但别为了“看起来高级”而绕过 CSS 原生能力。真正容易被忽略的是:动画结束后元素的最终状态是否稳定(比如 transform 是否归零)、以及大量元素同时动画时的性能兜底策略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











