跳到主要内容

CSS 动画从入门到上线:看懂关键帧动画的 @keyframes 与性能写法

讲清 CSS @keyframes 关键帧动画的写法,从 duration、timing-function、iteration 到淡入滑动弹跳三类常见动效,再到为什么只动 transform 和 opacity,附一段可直接粘贴的淡入 CSS。

发布于 作者 李雷
#CSS #动画 #前端 #keyframes #性能优化

CSS 动画从入门到上线:看懂关键帧动画的 @keyframes 与性能写法

很多人写动画卡在两件事上:一是 @keyframes 里到底该写 from/to 还是百分比,二是动画一上真机就掉帧。这篇把这两件事讲透,顺手给你一段能直接抄走的淡入动画 CSS。

@keyframes 是怎么定义一段动画的

一段 CSS 动画分两部分:用 @keyframes 定义"过程",再用 animation 简写把这个过程套到元素上。

@keyframes 里描述的是"某个时间点元素长什么样"。最简单的写法是 fromto,等价于 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,决定动画结束后元素停在哪。默认动画一结束就弹回播放前的样式,设成 forwardsboth 才会停在 100% 那一帧。入场动画忘了这个,元素就会"闪回"原位。

还有 direction,设成 alternate 能让动画正放完再倒放。无限循环时如果用默认的 normal,每次循环都会从 100% 硬跳回 0%,看着一顿一顿;换成 alternate 就是平滑往复了。

淡入、滑动、弹跳:三类最常用的动效

日常 UI 里反复出现的就这三类,记住套路基本够用:

  • 淡入(fade):opacity 从 0 到 1,常配一点 translateY 做成"上浮淡入",比纯透明度变化更有层次。
  • 滑动(slide):transform: translateXtranslateY 从屏幕外滑进来,适合侧边栏、抽屉、通知条。
  • 弹跳/脉冲(bounce/pulse):用 scale 或带回弹的关键帧让元素轻微过冲,常给 CTA 按钮做吸引注意的呼吸效果。

这三类我自己写的时候,几乎从不手算关键帧。我习惯在 CSS Keyframes 动画生成器 里把帧拖出来,旁边有真实元素实时跑,拖时间轴定格看某一帧,手感对了再复制 CSS。比起在浏览器里反复改数值刷新,这样省掉的来回试错时间是实打实的。

性能的核心:只动 transform 和 opacity

这是最值钱的一条经验。浏览器渲染一帧要经过布局(layout)、绘制(paint)、合成(composite)三步。

动画化 widthheighttopleft 这类属性,会让浏览器每一帧都重算布局,主线程被拖住,移动端立刻掉帧。

transform(平移、缩放、旋转)和 opacity 走的是合成线程,由 GPU 处理,不触发布局也不触发重绘。所以"让元素往右移 100px",别写 left: 100px 的动画,要写 transform: translateX(100px)。视觉效果一样,代价天差地别。

需要更复杂的位移变换时,可以先用 CSS Transform 生成器translatescalerotate 组合调好,再丢进关键帧。

真实例子:一段可直接粘贴的淡入动画

把上面讲的拼起来,这是一段生产可用的"上浮淡入",卡片入场后停在终点不闪回:

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card {
  animation: fade-up 400ms ease-out both;
}

注意三个细节:动画属性只用了 opacitytransform,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