Skip to content

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
visibility

2.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 动画?

答案:

  1. 使用 transformopacity 属性,只触发合成
  2. 使用 will-change 提前创建合成层
  3. 避免触发 Layout 和 Paint
  4. 使用 requestAnimationFrame

Q2: transform 为什么性能好?

答案: transform 只触发 Composite 阶段,由 GPU 处理,不经过主线程,不触发重排重绘。

Q3: will-change 的原理和注意事项?

答案:

  • 原理: 提前告知浏览器属性即将变化,让浏览器创建独立的合成层
  • 注意:
    1. 不要过度使用,每个合成层都占用内存
    2. 动画结束后应该移除
    3. 不要对大量元素使用

Q4: 什么是合成层?

答案: 合成层是 GPU 上的独立图层,有自己的绘制上下文,变化时只需重新合成而不需要重绘。触发条件包括:3D transform、will-change、opacity + animation 等。

前端面试知识库