CSS动画性能优化核心在于合理组织关键帧、优先使用transform/opacity等可硬件加速属性,避免触发重排重绘;animation-fill-mode: forwards需在关键帧中显式定义所有属性值;交错动画推荐用calc()配合自定义属性实现;alternate方向下100%关键帧会被反向视为0%,需注意属性反转效果。

@keyframes 不只是写个 from/to 就完事。真正用起来,容易卡在“动不起来”“动错方向”“性能掉帧”上——核心问题不在会不会写,而在关键帧怎么组织、动画属性怎么配、哪些 CSS 属性能被硬件加速。
多属性同步动画必须注意 transform 和 opacity 的组合
浏览器对 transform 和 opacity 做了单独的图层提升和合成优化,其他属性(比如 width、left、background-color)触发动画时会强制重排(reflow)或重绘(repaint),掉帧明显。
- ✅ 推荐写法:
transform: translateX(100px) scale(1.2) rotate(10deg); opacity: 0.8; - ❌ 避免混用:
left: 100px; width: 200px; background-color: #ff6b6b;(这些无法被 GPU 加速) - 如果必须改颜色,优先用
filter: hue-rotate()或opacity叠加,比直接改background-color更稳
animation-fill-mode: forwards 不等于“停在最后一帧”
很多人以为加了 forwards 动画就“定住”了,但实际效果取决于关键帧里有没有显式定义最终状态。
- 如果
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; } }——to没写transform,那forwards只保留opacity: 1,transform会回退到初始值(不是动画结束时的值) - 正确做法:每个关键帧里,**所有参与动画的属性都得写全**,哪怕值没变:
to { opacity: 1; transform: translateY(0); } -
forwards只是把最后一帧的声明应用到元素上,它不会“记住”中间计算出的值
交错动画(stagger)靠 :nth-child() + animation-delay 实现,但别硬写 10 个规则
给一串 .item 元素做逐个入场,手动写 :nth-child(1) 到 :nth-child(10) 很累,也难维护。
立即学习“前端免费学习笔记(深入)”;
- CSS 自带的
animation-delay支持 calc() 计算:.item { animation-delay: calc(var(--i, 0) * 0.1s); },再配合 JS 设置--i自定义属性,一行就能控制全部 - 纯 CSS 场景下,用
counter-increment+counter()无法生成数字用于 calc,所以还是得靠预设或 JS 注入 - 注意:负的
animation-delay(如-0.5s)会让动画从中间开始播,适合“滚动进入即播放到一半”的场景,但要确保关键帧覆盖该时间点的状态
animation-direction: alternate 时,100% 关键帧会被反向当作 0%
设了 animation-direction: alternate 后,第二次循环不是从头再走一遍,而是把原动画倒着播——但浏览器会把原 100% 当作新循环的 0%,原 0% 当作新循环的 100%。
- 这意味着:如果你的
@keyframes slide { 0% { left: 0; } 100% { left: 200px; } },在alternate下第二次循环会从left: 200px开始,往left: 0走 - 但如果关键帧里用了
transform,反向时旋转/缩放也会反转(rotate(360deg)变成rotate(0deg)→rotate(360deg),视觉上就是顺时针转完再逆时针转回来) - 想让奇偶次循环行为一致?别用
alternate,改用animation-iteration-count: 2+ JS 控制重播,或拆成两个独立动画
关键帧动画的复杂点不在语法,而在浏览器如何解释每一帧的样式快照、何时触发图层合成、以及 fill-mode 和 direction 如何与关键帧定义相互作用。写完别只看“动没动”,得 inspect 元素,看 computed style 里最终生效的值是不是你预期的。


















