CSS 动画进阶:从 transition 到 @keyframes 与 will-change
掌握 CSS 动画的核心原理与性能优化,打造流畅的页面交互体验
CSS 动画是前端交互体验的基石。从简单的 hover 过渡到复杂的连续动画,理解 transition、animation、缓动函数与浏览器渲染原理,才能写出既流畅又高效的动画代码。
一、transition:状态过渡
transition 是最基础的 CSS 动画方式,定义属性从一个值到另一个值的过渡过程。
1.1 基本语法
.box {
transition: transform 0.3s ease 0.1s;
/* 等价于:property | duration | timing-function | delay */
}
.box:hover {
transform: scale(1.1);
}
1.2 多属性过渡
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.5s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0,0,0,0.15);
background-color: #f8f9fa;
}
/* 或统一过渡 */
.card {
transition: all 0.3s ease; /* 不推荐:性能差 */
}
避免使用
transition: all,它会导致非动画属性也参与过渡计算,影响性能。
1.3 transition 的局限
transition 只能在两个状态间过渡,无法定义中间关键帧,也无法自动循环。需要更复杂的动画时,用 @keyframes。
二、@keyframes 与 animation
2.1 定义关键帧
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-30px); }
100% { transform: translateY(0); }
}
.element {
animation: bounce 1s ease-in-out infinite;
}
2.2 animation 简写属性
.element {
animation:
bounce /* name */
1s /* duration */
ease-in-out /* timing-function */
0s /* delay */
infinite /* iteration-count */
alternate /* direction */
both /* fill-mode */
running; /* play-state */
}
| 属性 | 说明 | 常用值 |
|---|---|---|
| animation-name | 关键帧名称 | 自定义标识符 |
| animation-duration | 持续时间 | 0.3s 1s |
| animation-timing-function | 缓动函数 | ease linear cubic-bezier() |
| animation-delay | 延迟开始 | 0s 0.5s |
| animation-iteration-count | 重复次数 | 1 3 infinite |
| animation-direction | 方向 | normal reverse alternate |
| animation-fill-mode | 填充模式 | none forwards backwards both |
| animation-play-state | 播放状态 | running paused |
2.3 百分比关键帧
@keyframes pulse {
0% { opacity: 1; transform: scale(1); }
25% { opacity: 0.8; transform: scale(1.05); }
50% { opacity: 0.6; transform: scale(1.1); }
75% { opacity: 0.8; transform: scale(1.05); }
100% { opacity: 1; transform: scale(1); }
}
2.4 多动画组合
.element {
animation:
bounce 1s ease-in-out infinite,
spin 3s linear infinite;
}
三、缓动函数深入
缓动函数决定了动画的”节奏感”,是区分业余动画和专业动画的关键。
3.1 内置缓动函数
| 函数 | 效果 | 典型场景 |
|---|---|---|
linear | 匀速 | 加载进度条 |
ease | 慢-快-慢(默认) | 通用过渡 |
ease-in | 慢入 | 元素退出 |
ease-out | 快出慢停 | 元素进入 |
ease-in-out | 两端慢中间快 | 位移动画 |
3.2 cubic-bezier 自定义曲线
.element {
/* 自定义贝塞尔曲线:x1, y1, x2, y2 */
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
四个参数定义两个控制点,形成动画的速度曲线。y 值可以超出 [0,1] 范围,产生”回弹”效果。
3.3 常用曲线预设
:root {
/* 弹性回弹 */
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* 丝滑减速 */
--ease-smooth: cubic-bezier(0.25, 0.1, 0.25, 1);
/* 强力弹出 */
--ease-snap: cubic-bezier(0.87, 0, 0.13, 1);
/* 惯性滑动 */
--ease-inertia: cubic-bezier(0.16, 1, 0.3, 1);
}
.modal {
transition: transform 0.4s var(--ease-bounce);
}
3.4 steps 阶跃函数
/* 精灵图动画 */
.sprite {
width: 64px;
height: 64px;
background: url('sprite.png') no-repeat;
animation: walk 0.8s steps(8) infinite;
}
@keyframes walk {
to { background-position: -512px 0; }
}
四、浏览器渲染与性能优化
4.1 动画性能的核心原则
浏览器渲染流程:JavaScript → Style → Layout → Paint → Composite
要让动画流畅(60fps,每帧 16.7ms),关键是避免触发 Layout(重排)和 Paint(重绘),只用 Composite(合成层)。
| 属性类型 | 触发阶段 | 性能 | 示例属性 |
|---|---|---|---|
| 布局属性 | Layout → Paint → Composite | 最差 | width height top left margin |
| 绘制属性 | Paint → Composite | 一般 | color background box-shadow |
| 合成属性 | Composite | 最佳 | transform opacity filter |
4.2 只动画 transform 和 opacity
/* 错误:触发重排 */
.bad {
transition: left 0.3s, top 0.3s;
}
.bad:hover { left: 100px; top: 50px; }
/* 正确:使用 transform,仅触发合成 */
.good {
transition: transform 0.3s;
}
.good:hover { transform: translate(100px, 50px); }
4.3 will-change 提前告知浏览器
.card {
will-change: transform;
}
/* 注意:不要滥用,仅在确实需要时使用 */
will-change 告诉浏览器某属性即将变化,浏览器会提前创建独立的合成层。但每个合成层都消耗内存,不应长期对所有元素设置。
最佳实践:动画开始前设置,结束后移除。
/* CSS 方案:hover 时设置 */
.card { will-change: auto; }
.card:hover { will-change: transform; }
/* JS 方案:动态控制 */
element.addEventListener('mouseenter', () => {
element.style.willChange = 'transform';
});
element.addEventListener('transitionend', () => {
element.style.willChange = 'auto';
});
4.4 contain 属性隔离重绘
.card-list {
contain: layout paint;
/* 或 */
contain: strict; /* 等同于 layout paint size style */
}
contain 告诉浏览器该元素的渲染独立于外部,改变该元素不会影响外部布局,从而减少重排范围。
4.5 硬件加速的陷阱
transform: translateZ(0) 或 will-change: transform 会触发硬件加速,创建 GPU 层。但过多 GPU 层会导致:
- 内存消耗增大
- 层合并开销
- 大面积图层反而降低性能
五、实战动画模式
5.1 模态框淡入缩放
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: opacity 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
}
.modal {
transform: scale(0.9) translateY(20px);
opacity: 0;
transition: transform 0.3s var(--ease-bounce), opacity 0.3s ease;
}
.modal-overlay.active .modal {
transform: scale(1) translateY(0);
opacity: 1;
}
5.2 列表交错入场
.list-item {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.5s ease forwards;
}
.list-item:nth-child(1) { animation-delay: 0.1s; }
.list-item:nth-child(2) { animation-delay: 0.2s; }
.list-item:nth-child(3) { animation-delay: 0.3s; }
.list-item:nth-child(4) { animation-delay: 0.4s; }
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
5.3 加载骨架屏脉冲
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
六、prefers-reduced-motion 无障碍
部分用户对动画敏感(前庭功能障碍、眩晕症),应尊重系统设置:
@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;
}
}
这条媒体查询会禁用所有动画,保留状态变化。它应该出现在全局样式中,确保覆盖所有动画。
6.1 条件性应用动画
.element {
/* 默认无动画 */
transition: none;
}
@media (prefers-reduced-motion: no-preference) {
.element {
transition: transform 0.3s ease;
}
}
这种写法更”积极”:默认无动画,仅当用户明确表示不介意时才启用。
七、调试与性能检测
7.1 Chrome DevTools 动画面板
- Animations 面板:录制和调节动画时间线
- Performance 面板:查看帧率(FPS)和渲染耗时
- Layers 面板:查看合成层结构
7.2 帧率检测
let lastTime = performance.now();
let frames = 0;
function checkFPS() {
frames++;
const now = performance.now();
if (now >= lastTime + 1000) {
console.log(`FPS: ${frames}`);
frames = 0;
lastTime = now;
}
requestAnimationFrame(checkFPS);
}
checkFPS();
7.3 性能优化清单
| 检查项 | 说明 |
|---|---|
| 是否只用 transform/opacity | 避免动画布局和绘制属性 |
| will-change 是否合理使用 | 动画时启用,结束后移除 |
| 动画是否在主线程 | 合成层动画不阻塞主线程 |
| 是否有过多合成层 | 检查 Layers 面板 |
| 是否支持 prefers-reduced-motion | 尊重用户系统设置 |
| 动画是否流畅(60fps) | Performance 面板查看掉帧情况 |
总结
CSS 动画的核心原则可以总结为一句话:用最简单的工具解决问题,用最少的属性触发渲染。
- 简单过渡用
transition,复杂序列用@keyframes - 缓动函数是动画质感的关键,善用
cubic-bezier - 性能优化的核心是只动画
transform和opacity will-change是双刃剑,按需使用- 始终尊重
prefers-reduced-motion - 用 DevTools 检测帧率,确保 60fps 流畅体验
动画不是炫技,而是提升用户体验的工具。流畅、适度、尊重用户偏好的动画,才是好动画。