css中transition-delay不支持nth-child内计算,必须手动写li:nth-child(n){transition-delay:0.xs}或用js+css变量;动画需用opacity+transform组合并写在动的元素上。

transition-delay 配合 nth-child 怎么写才不卡顿
直接给结论:用 nth-child(n) 算延迟值时,别用 transition-delay: 0.1s * n 这种写法——CSS 不支持运算,浏览器会直接忽略整个声明。
正确做法是手动展开或用 CSS 自定义属性 + JS 控制。纯 CSS 场景下,得老老实实写:
li:nth-child(1) { transition-delay: 0s; }
li:nth-child(2) { transition-delay: 0.1s; }
li:nth-child(3) { transition-delay: 0.2s; }
li:nth-child(4) { transition-delay: 0.3s; }- 超过 10 项就该考虑 JS 动态加 class,硬写 nth-child 易错且难维护
-
transition-delay值必须带单位(s或ms),写成0.1无效 - 动画触发前元素必须已有初始状态(比如
transform: translateY(20px)),否则延迟没意义
为什么 opacity + transform 比 display 或 visibility 更适合做滑入
display: none 一上来就脱离文档流,transition 根本不生效;visibility: hidden 虽保留占位,但 opacity 为 0 时仍无法触发动画起始帧。
真正能平滑过渡的组合只有:
立即学习“前端免费学习笔记(深入)”;
-
opacity: 0→opacity: 1 -
transform: translateY(20px)→transform: translateY(0)
这两者都是可被 GPU 加速的属性,不会触发重排(reflow)。而 height、margin、top 等会强制浏览器计算布局,列表项多时明显掉帧。
transition 写在哪个选择器上容易失效
常见错误:把 transition 写在父容器(比如 ul)上,指望子项自动继承——它不继承,也不会生效。
必须写在要动的元素本身上,也就是 li 或其内部的 div:
li {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.3s ease, transform 0.3s ease;
}- 别漏写多个属性时的逗号分隔,写成
transition: opacity 0.3s, transform 0.3s就行,不用重复ease - 如果用了
will-change: transform,记得只在动画前临时加,动画完立刻移除,否则长期占用内存 - 用
prefers-reduced-motion降级时,@media (prefers-reduced-motion: reduce)内要把transition设为none
JS 触发滑入时,offsetTop 或 getBoundingClientRect() 为什么取不到预期位置
因为 DOM 渲染有延迟。JS 插入 li 后立刻读取位置,此时样式还没应用,transform 还是初始值,getBoundingClientRect() 返回的是未动画前的坐标。
稳妥做法是等下一帧再触发动画类:
listItem.classList.add('is-entering');
requestAnimationFrame(() => {
listItem.classList.add('is-active');
});-
is-entering设初始状态(opacity: 0; transform: translateY(20px)) -
is-active设目标状态(opacity: 1; transform: translateY(0)),靠 class 切换触发 transition - 别用
setTimeout(..., 0),它不保证在渲染前执行,requestAnimationFrame才是正确时机
实际项目里,最常被忽略的是「动画类名的添加时机」和「transition 必须写在动的元素上」这两点。前者导致列表项集体闪一下再动,后者导致只有第一个动、后面全僵住。










