跳至主要內容

CSS 動畫效能最佳化:讓動畫絲滑流暢的技巧

CSS 動畫效能最佳化:讓動畫絲滑流暢的技巧

流暢的動畫效果能大幅提升使用者體驗,但動畫如果沒有注意效能,可能讓整個頁面卡頓甚至難以使用,在行動裝置上尤其明顯。這篇文章分享 CSS 動畫效能最佳化的核心技巧。

瀏覽器渲染流程基礎

瀏覽器渲染分為幾個步驟:

  1. Style:計算 CSS 樣式
  2. Layout:計算元素幾何位置(尺寸、位置)
  3. Paint:填充像素
  4. Composite:合成圖層

動畫優化的核心原則:盡量只觸發 Composite,避免 Layout 和 Paint

哪些屬性會觸發 Layout(重排)

/* 這些屬性的變更會觸發 Layout,動畫中應避免 */
.bad-animation {
  width: 200px;
  height: 100px;
  margin: 20px;
  top: 50px;
  left: 100px;
}

transform 和 opacity 是首選

/* 好的做法:只觸發 Composite */
.card {
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.card:hover {
  transform: translateY(-4px); /* 不觸發 Layout */
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

will-change 提示瀏覽器

.animated-card {
  will-change: transform; /* 提前建立 GPU 圖層 */
}

/* 動畫結束後移除,避免佔用過多記憶體 */
.animated-card.animation-done {
  will-change: auto;
}

注意:will-change 不要濫用,每個使用了它的元素都會佔用更多 GPU 記憶體。

實際案例:卡片懸停效果

/* 不好的版本(觸發 Layout) */
.card-bad:hover {
  margin-top: -4px; /* 觸發 Layout! */
}

/* 好的版本(只觸發 Composite) */
.card-good {
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
              box-shadow 0.3s ease;
  will-change: transform;
}

.card-good:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(0,0,0,0.2);
}

常用動畫範例

/* 旋轉載入指示器 */
@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 40px;
  height: 40px;
  border: 3px solid #f0f0f0;
  border-top-color: #3498db;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  will-change: transform;
}

/* 脈動效果 */
@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.05); opacity: 0.8; }
}

減少重排的 JavaScript 技巧

// 壞的做法:交替讀寫,每次都強制 Layout
element.style.width = '200px';
console.log(element.offsetWidth); // 強制 Layout
element.style.height = '100px';

// 好的做法:批量讀取再批量寫入
const width = element.offsetWidth;
const height = element.offsetHeight;
element.style.width = (width * 2) + 'px';
element.style.height = (height * 2) + 'px';

// 使用 requestAnimationFrame
function animate() {
  requestAnimationFrame(() => {
    element.style.transform = `translateX(${position}px)`;
    position += 2;
    if (position < 300) animate();
  });
}

CSS Custom Properties 管理動畫變數

:root {
  --duration-fast: 150ms;
  --duration-normal: 300ms;
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.button {
  transition: transform var(--duration-fast) var(--ease-spring);
}

.button:hover { transform: scale(1.05); }
.button:active { transform: scale(0.97); }

尊重使用者偏好設定

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

效能偵測工具

Chrome DevTools Performance 面板可以分析每帧耗時,目標維持在 16.7ms(60fps)以下。啟用 Rendering 面板中的 "Paint flashing" 可以視覺化重繪區域,找出效能問題所在。

分享這篇文章