CSS動畫View Transitions前端開發SPA2026

CSS View Transitions:原生頁面過渡動畫,2026 年生產就緒

CSS View Transitions API 完整教學!Same-document 與 Cross-document 語法、實際應用場景、與 Framer Motion/GSAP 比較。2026 年工程師必看的頁面過渡動畫指南!

· 7 分鐘閱讀

過去要實現頁面之間的流暢過渡動畫,工程師必須引入 Framer Motion、GSAP 這類 JavaScript 動畫庫。2025 年 10 月,CSS View Transitions 達到 Baseline(Baseline Newly Available),代表這項技術已經獲得所有主流瀏覽器的穩定支援。本文整理 View Transitions 的完整用法,讓你不需要任何額外依賴,就能做出流暢的頁面過渡效果。


為什麼前端工程師要關心 View Transitions

做過 SPA(Single Page Application)路由過渡動畫的工程師,都知道這件事有多麻煩:你要在每個頁面切換的時機點捕捉 DOM 狀態、計算動畫、執行過渡,還要處理返回(back navigation)的情境。

傳統解法有兩種:

  1. JavaScript 動畫庫(Framer Motion、GSAP):功能強大,但需要維護一個不小的 runtime,且你要自己處理「什麼時候觸發動畫」這件事。

  2. CSS Animation + class toggle:看起來簡單,但做起來複雜——你需要在路由層級自己計算新舊頁面的相對位置,计算 cross-fade 的時機。

View Transitions 的價值在於:瀏覽器自己處理了這一切。瀏覽器知道什麼時候頁面要切換、知道新舊狀態的快照(snapshot)、知道怎麼做 cross-fade。你只需要一行 API 呼叫。


View Transitions 的兩種類型

Same-Document:單一頁面內的狀態過渡

適用於 SPA 的路由切換、動態內容替換等場景。

// 觸發 Same-Document View Transition
document.startViewTransition(() => {
  // 在這個 callback 裡執行 DOM 更新
  // 瀏覽器會自動為更新前後的 DOM 建立快照,
  // 並在兩者之間執行 cross-fade 或自訂動畫
  updateDOM();
});
<!DOCTYPE html>
<html>
<head>
  <style>
    /* View Transition 的預設 cross-fade 動畫 */
    ::view-transition-old(root) {
      animation: 200ms ease-out fade-out;
    }

    ::view-transition-new(root) {
      animation: 200ms ease-in fade-in;
    }

    /* 如果你想控制整個頁面的過渡 */
    .view-transition-page {
      animation: 300ms ease both slide-from-right;
    }
  </style>
</head>
<body>
  <nav>
    <a href="/home" onclick="navigate(event)">Home</a>
    <a href="/about" onclick="navigate(event)">About</a>
  </nav>

  <main id="content"></main>

  <script>
    async function navigate(event) {
      event.preventDefault();
      const href = event.currentTarget.getAttribute('href');

      await document.startViewTransition(async () => {
        // 更新 DOM
        const page = await fetchPage(href);
        document.getElementById('content').innerHTML = page;
        // 更新 URL
        history.pushState({}, '', href);
      });
    }
  </script>
</body>
</html>

Cross-Document:頁面之間的過渡

適用於 MPA(Multi-Page Application),不同 HTML 文件之間的過渡。

<!-- page-a.html -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <!-- 重要:宣告同一個 origin 內的跨文件過渡 -->
  <meta name="view-transition" content="same-origin" />
</head>
<body>
  <header>
    <nav>
      <!-- 連結到同一 origin 內的其他頁面,會自動觸發 View Transition -->
      <a href="page-b.html">前往 B 頁面</a>
    </nav>
  </header>
</body>
</html>
<!-- page-b.html -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta name="view-transition" content="same-origin" />
  <style>
    /* 定義過渡動畫 */
    ::view-transition-old(root) {
      animation: 250ms cubic-bezier(0.4, 0, 0.2, 1) both fade-out,
                 250ms cubic-bezier(0.4, 0, 0.2, 1) 30ms both slide-to-left;
    }

    ::view-transition-new(root) {
      animation: 300ms cubic-bezier(0, 0, 0.2, 1) 100ms both fade-in,
                 300ms cubic-bezier(0, 0, 0.2, 1) 100ms both slide-from-right;
    }
  </style>
</head>
<body>
  <header>
    <nav>
      <a href="page-a.html">返回 A 頁面</a>
    </nav>
  </header>
</body>
</html>

關鍵:view-transition meta 標籤

這行 HTML 告訴瀏覽器:同一個 origin 內的 <a href> 連結點擊時,應該觸發 Cross-Document View Transition。不需要 JavaScript。


進階自訂:::view-transition-old 與 ::view-transition-new

預設的 cross-fade 適合大多數場景,但你可以透過 CSS 完全自訂過渡動畫。

自訂整頁過渡

/* 自訂滑動過渡 */
@keyframes slide-out-to-left {
  from { transform: translateX(0); opacity: 1; }
  to { transform: translateX(-30px); opacity: 0; }
}

@keyframes slide-in-from-right {
  from { transform: translateX(30px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

::view-transition-old(root) {
  animation: 200ms ease-out slide-out-to-left;
}

::view-transition-new(root) {
  animation: 200ms ease-in slide-in-from-right;
}

自訂特定元素的過渡(Element Transition)

View Transitions 最強大的功能之一:可以對特定元素定義專屬過渡動畫,而其他元素執行不同的過渡。

// 當某個產品卡被點擊時,
// 讓這張圖片執行「放大並移動到目標位置」的過渡
// 同時其他內容執行 cross-fade

document.startViewTransition(async () => {
  // 1. 為即將執行的圖片過渡命名
  const heroImage = document.querySelector('.product-card__image');

  // 2. 在 DOM 更新前,先標記這個元素
  if (heroImage) {
    heroImage.style.viewTransitionName = 'product-hero';
  }

  // 3. 執行 DOM 更新
  await fetchAndRenderProductPage(productId);
});
/* 圖片自己的過渡動畫:從小卡片位置過渡到全尺寸 */
::view-transition-old(product-hero) {
  animation: 300ms ease-out scale-down-to-normal;
}

::view-transition-new(product-hero) {
  animation: 300ms ease-out scale-up-from-small;
}

/* 其他元素執行預設過渡 */
::view-transition-old(root),
::view-transition-new(root) {
  animation: 200ms ease-out fade-out/fade-in;
}

實際應用場景:電子商務產品頁

電子商務網站的「從列表頁點擊商品,圖片放大過渡到詳情頁」是 View Transitions 最典型的應用場景:

// product-list.js
function handleProductClick(productId, imageElement) {
  // 為即將點擊的圖片設定 view-transition-name
  imageElement.style.viewTransitionName = `product-${productId}`;

  document.startViewTransition(async () => {
    // 移除 transition name,避免干擾
    imageElement.style.viewTransitionName = '';

    // 導航到詳情頁
    await navigateToProduct(productId);
  });
}
/* product-detail.css */
/* 圖片會從列表中的小尺寸,漸變為詳情頁的大尺寸 */
::view-transition-old(product-\d+) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-new(product-\d+) {
  animation: none;
  mix-blend-mode: normal;
}

瀏覽器支援(2026 年 3 月)

類型Chrome/EdgeSafariFirefox
Same-Document✅ 111+✅ 18+✅ 133+
Cross-Document✅ 126+✅ 18.2+🔄 開發中
view-transition meta✅ 126+✅ 18.2+🔄 開發中

Baseline 狀態:Same-document View Transitions 於 2025 年 10 月達到 Baseline(表示所有主流瀏覽器都已經穩定支援)。

Cross-Document 的 Firefox 支援仍在開發中,如果你的用戶有很大比例是 Firefox 使用者,你需要提供 fallback。

Feature Detection

// 檢查瀏覽器是否支援 View Transitions
if (!document.startViewTransition) {
  // Fallback:直接執行導航,不做過渡動畫
  navigateWithoutTransition();
  return;
}

// 支援:執行過渡動畫
await document.startViewTransition(() => navigate());
<!-- Cross-document 的 fallback: Progressive Enhancement -->
<!-- 在 Firefox 上,連結會正常跳轉,不會有 View Transition -->
<a href="/page-b.html" view-transition="same-origin">
  前往 B 頁面
</a>

與 JavaScript 動畫庫的比較

維度View TransitionsFramer MotionGSAP
學習曲線
依賴大小0(瀏覽器原生)~30KB~60KB
頁面間過渡✅ 原生支援❌ 需要自行處理❌ 需要自行處理
效能瀏覽器原生優化優秀優秀
控制粒度中等細緻極細緻
複雜動畫(如 FLIP)受限完全支援完全支援

結論

  • 簡單到中等的過渡 → View Transitions(首選)
  • 需要複雜 choreography、FLIP 動畫、手勢驅動 → Framer Motion 或 GSAP
  • 兩者也可以結合:View Transitions 處理頁面過渡,JS 庫處理頁面內的複雜動畫

實務建議:從哪里開始

立即可以在專案使用的場景

  1. SPA 路由過渡(Same-document)

    • Next.js、React Router、Vue Router 都可以整合
    • 最簡單的實作:在路由切換時包一層 document.startViewTransition
  2. 返回按鈕的過渡一致性

    • 過去返回時的動畫方向是個大麻煩
    • View Transitions 自動為 forward/back 導航提供不同的過渡方向
  3. 電子商務產品頁過渡

    • 「列表 → 詳情」的圖片放大效果,是 View Transitions 的旗艦級應用

整合到 React(Next.js App Router)

// app/page.tsx
'use client';

import { useRouter } from 'next/navigation';

export function ProductCard({ product }) {
  const router = useRouter();

  async function handleClick() {
    if (!document.startViewTransition) {
      router.push(`/products/${product.id}`);
      return;
    }

    await document.startViewTransition(async () => {
      router.push(`/products/${product.id}`);
    });
  }

  return (
    <button onClick={handleClick}>
      <img
        src={product.image}
        alt={product.name}
        style={{ viewTransitionName: `product-${product.id}` }}
      />
    </button>
  );
}

結語

View Transitions 的核心價值不是「讓你有更多動畫選項」,而是把本來需要你自己處理的頁面過渡邏輯,交給瀏覽器來處理

這個設計讓路由過渡從「需要工程師自己處理的事情」,變成「瀏覽器本身就知道怎麼做的預設行為」。如果你在做 SPA,而且你的用戶主要用 Chrome 或 Safari,View Transitions 現在就已經是生產就緒的選擇。

如果你需要更複雜的動畫(手勢、多物件 choreography),Framer Motion 和 GSAP 仍然是首選。但對於「滑動 cross-fade」、「從列表到詳情的圖片過渡」這類常見模式,View Transitions 已經足夠,而且零依賴。


延伸閱讀


本文是「2026 CSS 新技術」系列文章之一。