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 / to 是 0% / 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.5s,2s |
animation-timing-function | 缓动函数(同过渡) | ease,linear |
animation-delay | 开始前延迟 | 0.2s,-1s(负值=快进) |
animation-iteration-count | 播放次数 | 1(默认),3,infinite |
animation-direction | 播放方向 | 见下文 |
animation-fill-mode | 动画结束时的状态 | 见下文 |
animation-play-state | 播放/暂停 | running,paused |
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;
} 性能提示
与过渡相同,优先对 opacity 和 transform 做动画——它们只触发合成(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 媒体查询(见 响应式设计 章节)。