CSS 动画与性能
一、CSS 动画基础
1.1 Transition 过渡
css
.button {
background: #3498db;
transition: all 0.3s ease;
/* 完整写法 */
transition-property: background, transform;
transition-duration: 0.3s;
transition-timing-function: ease;
transition-delay: 0s;
}
.button:hover {
background: #2980b9;
transform: scale(1.05);
}1.2 缓动函数 (Timing Function)
css
.box {
/* 预设值 */
transition-timing-function: linear; /* 匀速 */
transition-timing-function: ease; /* 默认,慢-快-慢 */
transition-timing-function: ease-in; /* 慢-快 */
transition-timing-function: ease-out; /* 快-慢 */
transition-timing-function: ease-in-out; /* 慢-快-慢 */
/* 贝塞尔曲线 */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
/* 步进 */
transition-timing-function: steps(5, end);
}1.3 Animation 关键帧动画
css
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
.element {
animation: bounce 1s ease-in-out infinite;
/* 完整写法 */
animation-name: bounce;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite; /* 或数字 */
animation-direction: normal; /* reverse | alternate | alternate-reverse */
animation-fill-mode: forwards; /* none | forwards | backwards | both */
animation-play-state: running; /* paused */
}1.4 常用动画效果
css
/* 淡入 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* 滑入 */
@keyframes slideIn {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
/* 脉冲 */
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
/* 旋转 */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 抖动 */
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-10px); }
75% { transform: translateX(10px); }
}二、GPU 加速与合成层
2.1 渲染流程
JavaScript → Style → Layout → Paint → Composite
样式计算 布局 绘制 合成- Layout (回流/重排): 改变元素几何属性(宽高、位置)
- Paint (重绘): 改变外观属性(颜色、背景)
- Composite (合成): 只触发合成,性能最好
2.2 触发 GPU 加速的属性
css
/* 只触发 Composite,性能最佳 */
.gpu-accelerated {
transform: translateZ(0); /* 3D 变换 */
transform: translate3d(0, 0, 0);
will-change: transform;
opacity: 0.5; /* opacity 变化 */
}2.3 避免的属性
css
/* 触发 Layout (重排) - 性能最差 */
width, height, padding, margin, border
top, left, right, bottom
font-size, line-height
display, position
/* 触发 Paint (重绘) */
color, background, box-shadow
border-radius, outline
visibility2.4 动画性能优化
css
/* ❌ 差 - 触发重排 */
.bad {
animation: move-bad 1s;
}
@keyframes move-bad {
from { left: 0; }
to { left: 100px; }
}
/* ✅ 好 - 只触发合成 */
.good {
animation: move-good 1s;
}
@keyframes move-good {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}三、will-change 与性能
3.1 will-change 用法
css
/* 提前告知浏览器即将变化的属性 */
.element {
will-change: transform, opacity;
}
/* 悬停时才启用 */
.container:hover .element {
will-change: transform;
}
.element:active {
transform: scale(1.2);
}3.2 注意事项
css
/* ❌ 不要全局使用 */
* { will-change: transform; }
/* ❌ 不要过度使用 - 会消耗大量内存 */
.element {
will-change: transform, opacity, top, left, width, height;
}
/* ✅ 动态添加/移除 */
.element:hover {
will-change: transform;
}
/* ✅ JavaScript 控制 */
element.addEventListener('mouseenter', () => {
element.style.willChange = 'transform';
});
element.addEventListener('animationend', () => {
element.style.willChange = 'auto';
});四、高性能动画实践
4.1 使用 transform 代替位置属性
css
/* ❌ 避免 */
.modal { top: 50%; left: 50%; }
/* ✅ 推荐 */
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}4.2 使用 opacity 代替 visibility/display
css
/* ❌ 避免 */
.hidden { display: none; }
.show { display: block; }
/* ✅ 推荐 - 配合 pointer-events */
.hidden {
opacity: 0;
pointer-events: none;
}
.show {
opacity: 1;
pointer-events: auto;
transition: opacity 0.3s;
}4.3 减少动画元素数量
css
/* 使用伪元素减少 DOM */
.button::after {
content: '';
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.2);
transform: scaleX(0);
transition: transform 0.3s;
}
.button:hover::after {
transform: scaleX(1);
}4.4 使用 contain 隔离
css
/* 限制浏览器的渲染范围 */
.card {
contain: layout paint; /* 或 strict | content */
}五、requestAnimationFrame
5.1 基本用法
javascript
function animate() {
// 动画逻辑
element.style.transform = `translateX(${x}px)`;
if (x < targetX) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);5.2 与 setTimeout 对比
javascript
// ❌ setTimeout - 可能导致掉帧
setInterval(() => {
element.style.left = x++ + 'px';
}, 16);
// ✅ requestAnimationFrame - 与屏幕刷新同步
function animate() {
element.style.transform = `translateX(${x++}px)`;
requestAnimationFrame(animate);
}六、高频面试题
Q1: 如何实现 60fps 动画?
答案:
- 使用
transform和opacity属性,只触发合成 - 使用
will-change提前创建合成层 - 避免触发 Layout 和 Paint
- 使用
requestAnimationFrame
Q2: transform 为什么性能好?
答案: transform 只触发 Composite 阶段,由 GPU 处理,不经过主线程,不触发重排重绘。
Q3: will-change 的原理和注意事项?
答案:
- 原理: 提前告知浏览器属性即将变化,让浏览器创建独立的合成层
- 注意:
- 不要过度使用,每个合成层都占用内存
- 动画结束后应该移除
- 不要对大量元素使用
Q4: 什么是合成层?
答案: 合成层是 GPU 上的独立图层,有自己的绘制上下文,变化时只需重新合成而不需要重绘。触发条件包括:3D transform、will-change、opacity + animation 等。