CSS 动画从入门到上线:看懂关键帧动画的 @keyframes 与性能写法
讲清 CSS @keyframes 关键帧动画的写法,从 duration、timing-function、iteration 到淡入滑动弹跳三类常见动效,再到为什么只动 transform 和 opacity,附一段可直接粘贴的淡入 CSS。
CSS 动画从入门到上线:看懂关键帧动画的 @keyframes 与性能写法
很多人写动画卡在两件事上:一是 @keyframes 里到底该写 from/to 还是百分比,二是动画一上真机就掉帧。这篇把这两件事讲透,顺手给你一段能直接抄走的淡入动画 CSS。
@keyframes 是怎么定义一段动画的
一段 CSS 动画分两部分:用 @keyframes 定义"过程",再用 animation 简写把这个过程套到元素上。
@keyframes 里描述的是"某个时间点元素长什么样"。最简单的写法是 from 和 to,等价于 0% 和 100%:
@keyframes fade-up {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
当动画有中间状态时,就得用百分比。比如一个脉冲动画,峰值在正中间:
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.06); }
100% { transform: scale(1); }
}
百分比的好处是你能精确控制每个时刻的样子,0% 和 100% 不必对称。一个带回弹的滑入动画,可以让元素在 70% 处先冲过头一点,再落定:
@keyframes slide-in {
0% { transform: translateX(-100%); }
70% { transform: translateX(4%); }
100% { transform: translateX(0); }
}
duration、timing-function、iteration 各管什么
@keyframes 只说了"怎么变",但没说"多快、几次、用什么节奏"。这些交给 animation 简写:
.card {
animation: fade-up 400ms ease-out both;
}
逐个拆开:
- duration(时长):
400ms,一遍跑多久。入场动效一般 200ms 到 500ms,太长会让界面显得拖沓。 - timing-function(缓动):
ease-out,控制速度曲线。linear匀速适合转圈,ease-out起步快收尾慢适合入场,想要更精细的曲线可以用cubic-bezier()自定义。 - iteration-count(次数):不写默认 1 次,写
infinite就无限循环,转圈和脉冲都靠它。 - fill-mode:这里的
both,决定动画结束后元素停在哪。默认动画一结束就弹回播放前的样式,设成forwards或both才会停在 100% 那一帧。入场动画忘了这个,元素就会"闪回"原位。
还有 direction,设成 alternate 能让动画正放完再倒放。无限循环时如果用默认的 normal,每次循环都会从 100% 硬跳回 0%,看着一顿一顿;换成 alternate 就是平滑往复了。
淡入、滑动、弹跳:三类最常用的动效
日常 UI 里反复出现的就这三类,记住套路基本够用:
- 淡入(fade):
opacity从 0 到 1,常配一点translateY做成"上浮淡入",比纯透明度变化更有层次。 - 滑动(slide):
transform: translateX或translateY从屏幕外滑进来,适合侧边栏、抽屉、通知条。 - 弹跳/脉冲(bounce/pulse):用
scale或带回弹的关键帧让元素轻微过冲,常给 CTA 按钮做吸引注意的呼吸效果。
这三类我自己写的时候,几乎从不手算关键帧。我习惯在 CSS Keyframes 动画生成器 里把帧拖出来,旁边有真实元素实时跑,拖时间轴定格看某一帧,手感对了再复制 CSS。比起在浏览器里反复改数值刷新,这样省掉的来回试错时间是实打实的。
性能的核心:只动 transform 和 opacity
这是最值钱的一条经验。浏览器渲染一帧要经过布局(layout)、绘制(paint)、合成(composite)三步。
动画化 width、height、top、left 这类属性,会让浏览器每一帧都重算布局,主线程被拖住,移动端立刻掉帧。
而 transform(平移、缩放、旋转)和 opacity 走的是合成线程,由 GPU 处理,不触发布局也不触发重绘。所以"让元素往右移 100px",别写 left: 100px 的动画,要写 transform: translateX(100px)。视觉效果一样,代价天差地别。
需要更复杂的位移变换时,可以先用 CSS Transform 生成器 把 translate、scale、rotate 组合调好,再丢进关键帧。
真实例子:一段可直接粘贴的淡入动画
把上面讲的拼起来,这是一段生产可用的"上浮淡入",卡片入场后停在终点不闪回:
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: fade-up 400ms ease-out both;
}
注意三个细节:动画属性只用了 opacity 和 transform,GPU 友好;ease-out 让落位收尾自然;结尾的 both 保证卡片停在 translateY(0),不会动画一结束就跳回 16px 的起点。把这段抄进全局样式表,之后任何元素加上 animation: fade-up 400ms ease-out both 就能复用同一条入场。
如果想把 ease-out 换成更有个性的曲线,比如带一点回弹,可以去 cubic-bezier 缓动曲线编辑器 拖出贝塞尔控制点,生成的 cubic-bezier(...) 直接替换掉 ease-out 即可。
掌握 @keyframes 的百分比写法,记牢"只动 transform 和 opacity"这条底线,再用生成器把手感调对,CSS 动画从写到上线就不会再有意外掉帧。
Made by Toolora · Updated 2026-06-13