应使用 transform 和 opacity 实现列表滑动,因其仅触发合成不引发重排,而 top/left 会强制浏览器重算布局导致卡顿;实测 transform 动画帧率稳定在 58–60fps,top 则常跌至 20–30fps。

为什么用 transform 和 opacity 而不是 top/left
列表滑动卡顿的根源,往往是用了 top、left、margin 这类会触发重排(reflow)的属性。每次动画帧更新,浏览器都得重新计算整个布局树,尤其在长列表或低端设备上,单帧耗时轻松突破 16ms,直接掉帧。
而 transform(如 translateY)和 opacity 只影响合成层,由 GPU 处理,不打断主线程。实测中,transform: translateY() 动画在中端安卓机上稳定 58–60fps,换成 top 后常跌至 20–30fps。
- 永远避免在列表项动画中写
transition: top 0.3s或animation: slide-down 0.4s里用top - 用
transform: translateY(-100%)代替top: -100%实现“滑出视口” - 如果必须支持 IE10+ 且不用 JS 补丁,
transform: translate3d(0, 0, 0)比纯translateY更稳——它强制创建独立合成层
will-change 该不该加?加在哪?
will-change 不是“开加速器按钮”,它是给浏览器一个提前声明:“这个元素接下来要动了”。但滥用会导致内存暴涨、图层碎片化,反而拖慢滚动。
只对**明确即将进入动画状态的单个列表项**设置,且动画结束后应移除。例如:用户点击“展开详情”,此时才给对应 加 will-change: transform, opacity;动画结束回调里立刻设回 auto。
- ❌ 不要批量给整个
或所有加will-change: transform - ✅ 在 JS 触发动画前一刻设置:
item.style.willChange = 'transform, opacity' - ✅ 动画完成(监听
animationend或用setTimeout延迟 1 帧)后执行:item.style.willChange = 'auto'
如何让多个列表项滑入有节奏、不糊成一团
纯靠 CSS transition 很难控制交错延迟,尤其当列表项动态增删时。更可靠的方式是结合 @keyframes + JS 控制每个项的 animation-delay。
关键点在于:延迟值必须基于索引动态生成,且不能硬编码固定毫秒数。比如第 0 项延时 0ms,第 1 项 50ms,第 2 项 100ms……这样视觉上才是“波浪式”滑入。
- 定义统一动画:
@keyframes slideInUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } - JS 中为每个
设置内联样式:el.style.animationDelay = `${index * 50}ms` - 务必加
animation-fill-mode: backwards,否则初始态不会保持opacity: 0,会出现闪现 - 如果列表项高度不一,慎用
height动画——它必然触发重排;改用max-height+overflow: hidden是折中方案,但过渡需设足够大值(如max-height: 500px)
移动端真机测试最容易被忽略的三个细节
开发时在桌面 Chrome 看着丝滑,一上真机就卡顿,大概率栽在这三处:
- 未禁用
touch-action: manipulation:iOS Safari 默认会对滑动区域做额外事件处理,加touch-action: none给动画容器可减少响应延迟 - 父容器未设
contain: layout paint:当列表外还有其他动态内容(如顶部 banner),不加这个会导致浏览器不敢复用图层,频繁重绘 - 动画过程中仍响应
scroll事件并执行 JS:哪怕只是读取scrollTop,也会打断合成线程。用getBoundingClientRect()替代,或把 scroll 监听节流到requestIdleCallback
最隐蔽的坑是:动画元素的 z-index 层级过高,导致浏览器被迫提升整个渲染树的合成复杂度。能不用 z-index 就别用,优先靠 DOM 顺序和 transform: translateZ(0) 控制叠层。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











