CSS 动画

过渡(transition)只能在两个状态之间切换,而动画(animation)可以定义任意数量的关键帧,实现复杂、多阶段、循环的动画效果。

@keyframes — 关键帧

先定义动画的关键帧,再把它应用到元素上:

css
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.element {
  animation: fadeIn 0.5s ease;
}

from / to0% / 100% 的别名。

多阶段关键帧

css
@keyframes bounce {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.15);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

百分比表示动画进度的时间点。同一个百分比可以写多个共享属性。

animation 属性

简写

css
.element {
  animation: name duration timing-function delay iteration-count direction fill-mode;
}

分写

属性说明示例
animation-name引用的 @keyframes 名称fadeIn
animation-duration单次动画时长0.5s2s
animation-timing-function缓动函数(同过渡)easelinear
animation-delay开始前延迟0.2s-1s(负值=快进)
animation-iteration-count播放次数1(默认),3infinite
animation-direction播放方向见下文
animation-fill-mode动画结束时的状态见下文
animation-play-state播放/暂停runningpaused

animation-iteration-count — 循环次数

css
.once { animation-iteration-count: 1; }      /* 播放 1 次(默认) */
.three { animation-iteration-count: 3; }      /* 播放 3 次 */
.forever { animation-iteration-count: infinite; } /* 无限循环 */

animation-direction — 播放方向

效果
normal正向播放(默认)
reverse反向播放(从 100% 到 0%)
alternate奇数次正向、偶数次反向
alternate-reverse奇数次反向、偶数次正向
css
@keyframes swing {
  0%, 100% { transform: rotate(-5deg); }
  50% { transform: rotate(5deg); }
}
.swing {
  animation: swing 1s ease-in-out infinite alternate;
}

animation-fill-mode — 填充模式

控制动画播放前和结束后的状态:

效果
none不保留任何状态(默认)
forwards结束后保持最后一帧的样式
backwards延迟期间应用第一帧的样式
both同时拥有 forwards 和 backwards 的效果
css
.fade-in {
  animation: fadeIn 0.5s ease both;
  /* 播放前透明(0%的样式),播放后保持不透明(100%的样式) */
}

常见场景:页面加载时元素从透明渐入,结束后保持可见。使用 both 同时满足:动画延迟期间保持初始状态(backwards),动画结束后保持最终状态(forwards)。

animation-play-state — 播放控制

css
.running { animation-play-state: running; }
.paused { animation-play-state: paused; }

常用于悬停时暂停动画:

css
.carousel {
  animation: scroll 20s linear infinite;
}
.carousel:hover {
  animation-play-state: paused;
}

多个动画

一个元素可以同时应用多个动画,用逗号分隔:

css
.element {
  animation:
    fadeIn 0.5s ease both,
    slideUp 0.5s ease 0.2s both;
}

性能提示

与过渡相同,优先对 opacitytransform 做动画——它们只触发合成(compositing),不会引起重排(reflow)和重绘(repaint):

css
/* ✅ 高性能 */
@keyframes slide {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

/* ❌ 低性能 */
@keyframes bad-slide {
  from { left: -100%; }
  to { left: 0; }
}

实用的动画片段

加载旋转

css
@keyframes spin {
  to { transform: rotate(360deg); }
}
.spinner {
  width: 24px;
  height: 24px;
  border: 3px solid #e2e8f0;
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

骨架屏闪烁

css
@keyframes shimmer {
  0% { background-position: -200px 0; }
  100% { background-position: calc(200px + 100%) 0; }
}
.skeleton {
  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
  background-size: 200px 100%;
  animation: shimmer 1.5s ease-in-out infinite;
}

渐入渐出(与视图滚动配合)

css
@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
.reveal {
  animation: reveal ease both; /* 时长由滚动位置驱动,无需指定 duration */
  animation-timeline: view(); /* 滚动驱动动画(新特性,Chrome 115+) */
}

CSS 滚动驱动动画(Scroll-driven Animations)是较新的特性,让动画的进度与滚动位置绑定——不需要 JavaScript 即可实现”滚动到视口时渐入”的效果。目前仅 Chrome/Edge 完整支持,使用时需考虑优雅降级。

动画虽好,不要滥用。过多的动画会让页面感觉”吵闹”、分散注意力,且可能对前庭功能障碍的用户造成不适。确保你的动画遵循 prefers-reduced-motion 媒体查询(见 响应式设计 章节)。