CSS 动画概述
CSS动画是Web开发中提升用户体验的重要手段。通过CSS Transitions(过渡)和CSS Animations(动画),可以实现流畅的视觉效果,无需依赖JavaScript。正确使用动画可以引导用户注意力、提供交互反馈、增强界面的层次感。
CSS Transitions(过渡)
CSS过渡可以在属性值变化时创建平滑的动画效果。它适用于状态切换(如hover、focus)时的动画。
transition 属性详解
.button {
background-color: #3b82f6;
color: white;
padding: 12px 24px;
border: none;
border-radius: 8px;
/* transition 简写 */
transition: all 0.3s ease;
/* 或分别指定 */
/* transition-property: background-color, transform; */
/* transition-duration: 0.3s; */
/* transition-timing-function: ease; */
/* transition-delay: 0s; */
}
.button:hover {
background-color: #2563eb;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4);
}
.button:active {
transform: translateY(0);
}
transition-property — 过渡属性
| 值 | 说明 |
|---|---|
all |
所有可动画属性都会过渡(性能警告) |
none |
不应用过渡 |
transform |
仅过渡transform属性(推荐,GPU加速) |
opacity |
仅过渡透明度(推荐,GPU加速) |
background-color, color |
指定多个属性,逗号分隔 |
transition-timing-function — 缓动函数
/* 预设缓动函数 */
.ease { transition-timing-function: ease; } /* 默认:慢-快-慢 */
.ease-in { transition-timing-function: ease-in; } /* 慢速开始 */
.ease-out { transition-timing-function: ease-out; } /* 慢速结束 */
.ease-in-out { transition-timing-function: ease-in-out; } /* 慢速开始和结束 */
.linear { transition-timing-function: linear; } /* 匀速 */
/* 贝塞尔曲线自定义 */
.custom {
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* 弹性效果 */
.bounce {
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
实用过渡模式
/* 淡入效果 */
.fade-in {
opacity: 0;
transition: opacity 0.4s ease;
}
.fade-in.visible {
opacity: 1;
}
/* 滑入效果 */
.slide-up {
transform: translateY(20px);
opacity: 0;
transition: transform 0.4s ease, opacity 0.4s ease;
}
.slide-up.visible {
transform: translateY(0);
opacity: 1;
}
/* 缩放效果 */
.scale:hover {
transform: scale(1.05);
transition: transform 0.2s ease;
}
/* 下划线动画 */
.link {
position: relative;
text-decoration: none;
}
.link::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background: var(--accent);
transition: width 0.3s ease;
}
.link:hover::after {
width: 100%;
}
CSS Animations(动画)
CSS动画通过 @keyframes 定义关键帧,可以创建更复杂、更灵活的动画效果,支持自动播放、循环、延迟等。
@keyframes 定义
/* 基本淡入动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 使用百分比定义多阶段动画 */
@keyframes slideIn {
0% {
transform: translateX(-100%);
opacity: 0;
}
50% {
opacity: 1;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
/* 旋转动画 */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 脉冲动画 */
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
animation 属性详解
.animated-element {
/* animation 简写 */
animation: fadeIn 0.6s ease-out forwards;
/* 或分别指定 */
animation-name: fadeIn;
animation-duration: 0.6s;
animation-timing-function: ease-out;
animation-delay: 0.2s;
animation-fill-mode: forwards; /* 保持结束状态 */
animation-iteration-count: 1; /* 播放一次 */
animation-direction: normal; /* 正常方向 */
}
/* animation-fill-mode 说明 */
.fill-none { animation-fill-mode: none; } /* 默认:不应用初始/结束状态 */
.fill-forwards { animation-fill-mode: forwards; } /* 保持结束状态 */
.fill-backwards { animation-fill-mode: backwards; } /* 应用初始状态(包括delay期间) */
.fill-both { animation-fill-mode: both; } /* 两者都应用 */
animation-iteration-count 和 direction
/* 无限循环 */
.loading-spinner {
animation: spin 1s linear infinite;
}
/* 播放3次 */
.retry-button {
animation: shake 0.5s ease-in-out 3;
}
/* 交替方向 */
.alternating {
animation: pulse 2s ease-in-out infinite alternate;
}
/* 反向播放 */
.reverse {
animation: slideIn 0.6s ease-out reverse;
}
CSS Transform 变换
Transform是CSS动画的核心,可以对元素进行2D和3D变换。
2D Transform
.rotate {
transform: rotate(45deg);
}
.scale {
transform: scale(1.5);
}
.translate {
transform: translate(20px, 10px);
}
.skew {
transform: skew(10deg, 5deg);
}
/* 组合变换 */
.combined {
transform: rotate(45deg) scale(1.5) translate(20px, 0);
}
3D Transform
/* 3D空间需要父容器设置perspective */
.perspective-container {
perspective: 1000px;
}
.card-3d {
transform-style: preserve-3d;
transition: transform 0.6s ease;
}
.card-3d:hover {
transform: rotateY(15deg) rotateX(5deg);
}
/* 翻转效果 */
.flip-card {
position: relative;
width: 200px;
height: 200px;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front,
.flip-card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
}
.flip-card-back {
transform: rotateY(180deg);
}
性能优化技巧
CSS动画的性能至关重要,错误的选择会导致页面卡顿。
GPU加速与will-change
/* 只动画 transform 和 opacity(合成层属性,GPU加速) */
.performance-optimized {
transition: transform 0.3s ease, opacity 0.3s ease;
}
/* will-change 提示浏览器准备动画 */
.will-animate {
will-change: transform, opacity;
}
/* 动画结束后移除will-change */
.animated.done {
will-change: auto;
}
| 属性类型 | 包含属性 | 性能影响 |
|---|---|---|
| 合成层属性(推荐) | transform, opacity |
低:GPU加速,不触发重排重绘 |
| 绘制属性 | color, background, box-shadow |
中:触发重绘,但不触发重排 |
| 布局属性 | width, height, margin, padding |
高:触发重排(最昂贵的操作) |
黄金法则:尽量只动画transform和opacity,避免动画width、height、margin、top、left等布局属性。
实用动画模式
加载动画
/* 旋转加载器 */
@keyframes spinner {
to { transform: rotate(360deg); }
}
.loader {
width: 40px;
height: 40px;
border: 4px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spinner 0.8s linear infinite;
}
/* 骨架屏闪烁 */
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.skeleton {
background: linear-gradient(90deg,
var(--bg-elevated) 25%,
var(--bg-hover) 50%,
var(--bg-elevated) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
}
入场动画
/* 交错入场效果 */
.stagger-item {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.5s ease forwards;
}
.stagger-item:nth-child(1) { animation-delay: 0.1s; }
.stagger-item:nth-child(2) { animation-delay: 0.2s; }
.stagger-item:nth-child(3) { animation-delay: 0.3s; }
.stagger-item:nth-child(4) { animation-delay: 0.4s; }
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
/* 使用JS Intersection Observer 触发 */
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.animate-on-scroll').forEach(el => {
observer.observe(el);
});
</script>
微交互
/* 按钮点击波纹效果 */
.ripple-button {
position: relative;
overflow: hidden;
}
.ripple-button::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.4s ease, height 0.4s ease;
}
.ripple-button:active::after {
width: 300px;
height: 300px;
}
/* 数字计数动画(配合CSS counter) */
@keyframes countUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.counter {
animation: countUp 0.3s ease-out;
font-variant-numeric: tabular-nums;
}
减少动画的无障碍考虑
/* 尊重用户的减少动画偏好 */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* 或者只禁用特定动画 */
@media (prefers-reduced-motion: reduce) {
.animated-element {
animation: none;
transition: none;
}
}
最佳实践总结
- 只动画 transform 和 opacity,避免触发重排的属性
- 使用 will-change 提示浏览器准备动画(但不要滥用)
- 动画时长控制在300-500ms,太慢会让人等待,太快看不清
- 使用 ease-out 作为入场动画的缓动,ease-in 用于退场
- 始终添加
prefers-reduced-motion媒体查询 - 为动画元素预留空间,避免布局偏移
- 使用CSS变量管理动画参数,便于统一调整
- 在动画结束后清理:移除 will-change、清理类名