Day 17: CSS 动画 (CSS Animation)
🎯 学习目标
- 理解 CSS 动画的基本概念和原理
- 掌握
@keyframes规则的使用方法 - 学会使用
animation简写属性 - 能够创建复杂的动画效果和过渡
- 了解性能优化和最佳实践
💡 核心概念
什么是 CSS 动画?
CSS 动画 是一种使用 CSS 创建元素动态效果的技术,可以让元素从一个样式逐渐变化到另一个样式,创建流畅的视觉体验。
CSS 动画 vs CSS 过渡
| 特性 | CSS 动画 (Animation) | CSS 过渡 (Transition) |
|---|---|---|
| 复杂度 | 可以创建复杂的多阶段动画 | 只能在两个状态之间过渡 |
| 控制力 | 可以暂停、反转、控制播放进度 | 较少的控制选项 |
| 关键帧 | 支持多个关键帧 | 只有起始和结束状态 |
| JavaScript 交互 | 提供丰富的 API 接口 | 较少的 API 支持 |
| 性能 | 同样基于 GPU 加速 | 同样基于 GPU 加速 |
| 适用场景 | 复杂动画序列、循环动画 | 简单的状态变化 |
动画的基本组成部分
/* 1. 定义关键帧 */
@keyframes animationName {
from { /* 起始状态 */ }
to { /* 结束状态 */ }
}
/* 2. 应用动画 */
.element {
animation: animationName 2s ease-in-out infinite;
}
📝 动画属性详解
1. @keyframes – 定义关键帧
语法:
@keyframes animationName {
/* 百分比表示时间点 */
0% { /* 起始状态 */ }
50% { /* 中间状态 */ }
100% { /* 结束状态 */ }
/* 也可以使用 from 和 to */
from { /* 等同于 0% */ }
to { /* 等同于 100% */ }
}
示例:
/* 淡入效果 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* 移动效果 */
@keyframes moveRight {
0% {
transform: translateX(0);
}
50% {
transform: translateX(100px);
}
100% {
transform: translateX(200px);
}
}
/* 多属性变化 */
@keyframes complex {
0% {
transform: scale(1) rotate(0deg);
opacity: 0;
}
50% {
transform: scale(1.2) rotate(180deg);
opacity: 1;
}
100% {
transform: scale(1) rotate(360deg);
opacity: 0;
}
}
2. animation-name – 动画名称
指定要应用的动画名称(@keyframes 定义的名字)。
.element {
animation-name: fadeIn;
}
3. animation-duration – 动画时长
设置动画完成一个周期所需的时间。
.element {
animation-duration: 2s; /* 2秒 */
animation-duration: 1000ms; /* 1000毫秒 */
}
4. animation-timing-function – 时间函数
控制动画的速度曲线。
常用值:
/* 线性速度 */
animation-timing-function: linear;
/* 缓动效果 */
animation-timing-function: ease; /* 默认值 */
animation-timing-function: ease-in; /* 慢速开始 */
animation-timing-function: ease-out; /* 慢速结束 */
animation-timing-function: ease-in-out; /* 慢速开始和结束 */
/* 自定义贝塞尔曲线 */
animation-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
/* 阶梯函数(离散步骤) */
animation-timing-function: steps(4); /* 4个步骤 */
animation-timing-function: steps(4, end); /* 在每个步骤结束跳变 */
animation-timing-function: steps(4, start); /* 在每个步骤开始跳变 */
可视化时间曲线:
linear: ─────────────
ease: ╱────────╲
ease-in: ╱──────────
ease-out: ──────────╲
ease-in-out:╱───────╲
5. animation-delay – 动画延迟
设置动画开始前的延迟时间。
.element {
animation-delay: 1s; /* 延迟1秒后开始 */
animation-delay: -0.5s; /* 立即开始,从中间开始播放 */
}
6. animation-iteration-count – 迭代次数
设置动画播放的次数。
.element {
animation-iteration-count: 3; /* 播放3次 */
animation-iteration-count: infinite; /* 无限循环 */
animation-iteration-count: 2.5; /* 播放2.5次 */
}
7. animation-direction – 动画方向
设置动画的播放方向。
.element {
/* 正常方向(默认) */
animation-direction: normal;
/* 反向播放 */
animation-direction: reverse;
/* 交替方向:正-反-正-反... */
animation-direction: alternate;
/* 反向交替:反-正-反-正... */
animation-direction: alternate-reverse;
}
8. animation-fill-mode – 填充模式
设置动画在播放前后的样式。
.element {
/* 默认值:动画结束后回到初始状态 */
animation-fill-mode: none;
/* 动画开始前保持第一帧样式 */
animation-fill-mode: backwards;
/* 动画结束后保持最后一帧样式 */
animation-fill-mode: forwards;
/* 同时应用 backwards 和 forwards */
animation-fill-mode: both;
}
填充模式对比:
none: [初始] ──动画──> [回到初始]
backwards: [第一帧] ──动画──> [回到初始]
forwards: [初始] ──动画──> [最后一帧]
both: [第一帧] ──动画──> [最后一帧]
9. animation-play-state – 播放状态
控制动画的播放和暂停。
.element {
animation-play-state: running; /* 播放(默认) */
animation-play-state: paused; /* 暂停 */
}
/* 悬停时暂停动画 */
.element:hover {
animation-play-state: paused;
}
10. animation – 简写属性
一次性设置所有动画属性。
/* 完整语法 */
animation: name duration timing-function delay iteration-count direction fill-mode play-state;
/* 示例 */
animation: fadeIn 2s ease-in-out 1s infinite alternate forwards running;
/* 分开写更清晰 */
animation-name: fadeIn;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;
注意: 简写时,duration 必须在 delay 之前!
🎮 实战示例
示例 1: 基础淡入动画
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>基础淡入动画</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: 'Arial', sans-serif;
}
.container {
text-align: center;
}
.box {
width: 200px;
height: 200px;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
color: #333;
/* 应用动画 */
animation: fadeInScale 1.5s ease-out forwards;
opacity: 0; /* 初始不可见 */
}
@keyframes fadeInScale {
0% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.delay-1 { animation-delay: 0.2s; }
.delay-2 { animation-delay: 0.4s; }
.delay-3 { animation-delay: 0.6s; }
</style>
</head>
<body>
<div class="container">
<div class="box delay-1">Hello!</div>
<div class="box delay-2">CSS</div>
<div class="box delay-3">Animation</div>
</div>
</body>
</html>
示例 2: 旋转加载动画
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>旋转加载动画</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #1a1a2e;
}
.loader {
width: 100px;
height: 100px;
position: relative;
}
.loader::before,
.loader::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
border: 4px solid transparent;
}
.loader::before {
border-top-color: #00d2ff;
border-right-color: #00d2ff;
animation: spin 1s linear infinite;
}
.loader::after {
border-bottom-color: #ff0055;
border-left-color: #ff0055;
animation: spin 1.5s linear infinite reverse;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loading-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 14px;
font-weight: bold;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
</style>
</head>
<body>
<div class="loader">
<span class="loading-text">Loading...</span>
</div>
</body>
</html>
示例 3: 悬浮卡片效果
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>悬浮卡片效果</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
font-family: 'Arial', sans-serif;
}
.card {
width: 300px;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.card:hover {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
.card-image {
width: 100%;
height: 200px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 48px;
}
.card-content {
padding: 20px;
}
.card-title {
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.card-description {
color: #666;
line-height: 1.6;
}
.card-button {
display: inline-block;
margin-top: 15px;
padding: 10px 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-decoration: none;
border-radius: 25px;
font-weight: bold;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card-button:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.card-button:active {
transform: scale(0.95);
}
</style>
</head>
<body>
<div class="card">
<div class="card-image">🎨</div>
<div class="card-content">
<h2 class="card-title">CSS 动画</h2>
<p class="card-description">
悬停在卡片上查看浮动效果。CSS 动画让网页更加生动有趣!
</p>
<a href="#" class="card-button">了解更多</a>
</div>
</div>
</body>
</html>
示例 4: 打字机效果
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>打字机效果</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #1e1e1e;
font-family: 'Courier New', monospace;
}
.typewriter {
font-size: 32px;
color: #00ff00;
white-space: nowrap;
overflow: hidden;
border-right: 3px solid #00ff00;
animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite;
}
@keyframes typing {
from {
width: 0;
}
to {
width: 100%;
}
}
@keyframes blink-caret {
from,
to {
border-color: transparent;
}
50% {
border-color: #00ff00;
}
}
.container {
text-align: center;
}
h1 {
color: white;
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="container">
<h1>打字机效果</h1>
<div class="typewriter">Hello, World! This is a typewriter effect.</div>
</div>
</body>
</html>
示例 5: 波浪动画
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>波浪动画</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #00c6ff 0%, #0072ff 100%);
font-family: 'Arial', sans-serif;
}
.wave-container {
position: relative;
width: 200px;
height: 200px;
}
.wave {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.5);
animation: wave 3s linear infinite;
}
.wave:nth-child(1) {
animation-delay: 0s;
}
.wave:nth-child(2) {
animation-delay: 0.5s;
}
.wave:nth-child(3) {
animation-delay: 1s;
}
.wave:nth-child(4) {
animation-delay: 1.5s;
}
.wave:nth-child(5) {
animation-delay: 2s;
}
@keyframes wave {
0% {
transform: scale(0.5);
opacity: 1;
}
100% {
transform: scale(2);
opacity: 0;
}
}
.center-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 48px;
color: white;
}
</style>
</head>
<body>
<div class="wave-container">
<div class="wave"></div>
<div class="wave"></div>
<div class="wave"></div>
<div class="wave"></div>
<div class="wave"></div>
<div class="center-icon">🌊</div>
</div>
</body>
</html>
示例 6: 3D 翻转卡片
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 翻转卡片</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
font-family: 'Arial', sans-serif;
perspective: 1000px;
}
.flip-card {
width: 300px;
height: 400px;
position: relative;
transform-style: preserve-3d;
transition: transform 0.8s;
cursor: pointer;
}
.flip-card:hover {
animation: flip 1s ease-in-out forwards;
}
@keyframes flip {
0% {
transform: rotateY(0deg);
}
100% {
transform: rotateY(180deg);
}
}
.flip-card-front,
.flip-card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.flip-card-front {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.flip-card-back {
background: white;
color: #333;
transform: rotateY(180deg);
}
.card-icon {
font-size: 80px;
margin-bottom: 20px;
}
.card-title {
font-size: 28px;
font-weight: bold;
margin-bottom: 10px;
}
.card-text {
font-size: 16px;
text-align: center;
padding: 20px;
line-height: 1.6;
}
.card-button {
margin-top: 20px;
padding: 12px 24px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s;
}
.card-button:hover {
transform: scale(1.05);
}
</style>
</head>
<body>
<div class="flip-card">
<div class="flip-card-front">
<div class="card-icon">🎴</div>
<div class="card-title">悬停翻转</div>
<div class="card-text">把鼠标放在卡片上查看背面</div>
</div>
<div class="flip-card-back">
<div class="card-icon">✨</div>
<div class="card-title">3D 翻转效果</div>
<div class="card-text">
这是一个使用 CSS 3D 变换和动画创建的翻转卡片效果。
</div>
<button class="card-button">点击了解</button>
</div>
</div>
</body>
</html>
⚠️ 重要注意事项
1. 性能优化
最佳实践:
/* ✅ 推荐:使用 transform 和 opacity */
.element {
animation: move 2s ease-in-out;
}
@keyframes move {
to {
transform: translateX(100px);
opacity: 0.5;
}
}
/* ❌ 避免:使用 left/top/width/height */
.element {
animation: move 2s ease-in-out;
}
@keyframes move {
to {
left: 100px; /* 触发重排,性能差 */
width: 200px;
}
}
原因: transform 和 opacity 可以由 GPU 加速,不会触发重排(reflow)。
2. will-change 提示浏览器
/* 提示浏览器该元素将会变化,浏览器可以提前优化 */
.element {
will-change: transform, opacity;
}
/* 动画结束后清除 */
.element.finished {
will-change: auto;
}
注意: 不要滥用 will-change,只在需要时使用。
3. 减少重绘和重排
/* ✅ 好:使用复合层 */
.element {
transform: translateZ(0); /* 创建新的复合层 */
animation: slide 2s ease-in-out;
}
/* ❌ 差:频繁触发重排 */
.element {
animation: changeSize 2s ease-in-out;
}
@keyframes changeSize {
to {
width: 200px; /* 触发重排 */
height: 200px;
}
}
4. 动画填充模式
/* 保持动画结束后的状态 */
.element {
animation: fadeIn 1s ease-out forwards;
}
/* 避免:动画结束后元素回到初始状态 */
.element {
animation: fadeIn 1s ease-out; /* 默认 fill-mode: none */
}
5. 响应式设计
/* 在小屏幕上减少动画效果 */
@media (max-width: 768px) {
.element {
animation: none; /* 禁用动画 */
}
}
/* 或者减少动画时长 */
@media (max-width: 768px) {
.element {
animation-duration: 0.5s; /* 更快的动画 */
}
}
6. 可访问性
/* 尊重用户的动画偏好设置 */
@media (prefers-reduced-motion: reduce) {
.element {
animation: none !important;
transition: none !important;
}
}
🎓 实战应用场景
场景 1: 加载动画
.spinner {
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
场景 2: 通知提示动画
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
background: #4caf50;
color: white;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
animation: slideIn 0.5s ease-out, fadeOut 0.5s ease-in 2.5s forwards;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes fadeOut {
to {
opacity: 0;
transform: translateY(-20px);
}
}
场景 3: 按钮点击效果
.button {
padding: 10px 20px;
background: #3498db;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.button::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.button:active::before {
width: 300px;
height: 300px;
}
✍️ 练习任务
基础练习
- 淡入动画: 创建一个淡入动画,元素从透明变为不透明
- 移动动画: 创建一个元素从左到右移动的动画
- 旋转动画: 创建一个元素持续旋转的动画
- 缩放动画: 创建一个元素从小变大的动画
进阶练习
- 组合动画: 创建一个同时进行旋转、缩放和移动的动画
- 关键帧动画: 创建一个使用多个关键帧的复杂动画
- 交替动画: 创建一个使用
alternate方向的往复动画 - 无限循环: 创建一个无限循环的加载动画
实战挑战
- 导航菜单: 创建一个带有动画效果的下拉菜单
- 卡片翻转: 创建一个 3D 翻转卡片效果
- 进度条: 创建一个带有动画效果的进度条
- 粒子效果: 创建一个简单的粒子爆炸效果
💡 常见问题 FAQ
Q1: CSS 动画和 JavaScript 动画有什么区别?
A:
- CSS 动画: 声明式、性能好、易于实现、控制较少
- JavaScript 动画: 命令式、更灵活、控制力强、可能性能较差
选择建议:
- 简单动画 → CSS 动画
- 复杂交互 → JavaScript 动画
- 最佳实践 → 结合使用(CSS 处理简单动画,JS 处理复杂逻辑)
Q2: 如何调试 CSS 动画?
A:
- 使用浏览器开发者工具(F12)的 "Animations" 面板
- 调整动画速度:
animation-duration: 10s(减慢速度) - 使用
animation-play-state: paused暂停动画 - 检查关键帧:在开发者工具中查看 @keyframes 规则
Q3: 动画性能不好怎么办?
A:
- 使用
transform和opacity(GPU 加速) - 避免使用
left、top、width、height - 使用
will-change提示浏览器 - 减少动画元素数量
- 使用
requestAnimationFrame(JavaScript)
Q4: 如何让动画只在第一次加载时播放?
A:
.element {
animation: fadeIn 1s ease-out forwards;
}
/* 使用 JavaScript 添加类名 */
.element.animated {
animation: fadeIn 1s ease-out forwards;
}
// JavaScript
element.classList.add('animated');
Q5: 动画结束后如何执行 JavaScript 代码?
A:
element.addEventListener('animationend', () => {
console.log('动画结束');
// 执行后续操作
});
Q6: 如何创建复杂的多阶段动画?
A:
@keyframes complex {
0% {
transform: scale(1) rotate(0deg);
opacity: 0;
}
25% {
transform: scale(1.2) rotate(90deg);
opacity: 1;
}
50% {
transform: scale(1) rotate(180deg);
background: #ff0000;
}
75% {
transform: scale(1.2) rotate(270deg);
background: #00ff00;
}
100% {
transform: scale(1) rotate(360deg);
opacity: 0;
}
}
Q7: 动画在移动设备上卡顿怎么办?
A:
- 减少动画复杂度
- 使用
transform代替其他属性 - 减少同时动画的元素数量
- 在小屏幕上禁用动画
- 使用
will-change创建新的复合层
📚 拓展阅读
- MDN 文档: CSS Animations
- caniuse: 查看浏览器兼容性
- 相关主题: CSS Transitions、CSS Transforms、JavaScript Web Animations API
📝 总结
CSS 动画 是创建网页动态效果的重要工具,掌握后可以让你的网页更加生动有趣。
关键要点:
- 使用
@keyframes定义动画关键帧 - 使用
animation属性应用和控制动画 - 优先使用
transform和opacity优化性能 - 考虑可访问性和用户体验
- 合理使用
animation-fill-mode保持动画状态
最佳实践:
- ✅ 使用 GPU 加速属性(transform、opacity)
- ✅ 使用
will-change提示浏览器 - ✅ 尊重用户的动画偏好设置
- ✅ 在移动设备上简化动画
- ❌ 避免使用触发重排的属性(left、top、width、height)
下一课: Day 18 – 响应式设计与媒体查询