ThisWeb Logo
This.Web
所有文章React 效能優化實戰課
  1. 首頁
  2. 所有文章
  3. 前端
  4. Next.js Page Transition 實作:整合 View Transition API 與 JavaScript 頁面轉場

Next.js Page Transition 實作:整合 View Transition API 與 JavaScript 頁面轉場

前端

Kun | ThisWeb

資深前端工程師

發佈/更新於

2026年5月25日

免費訂閱電子報!

和 2000+ 工程師一起學習軟體、AI 開發技巧,每週一收穫 1 篇技術內容、1 段職涯分享、1 個最新資訊!

免費訂閱電子報!

和 2000+ 工程師一起學習軟體、AI 開發技巧,每週一收穫 1 篇技術內容、1 段職涯分享、1 個最新資訊!

Page Transition(頁面轉場) 一直是很流行的網頁技術和效果,他指的是使用者從一個頁面切換到另一個頁面時,中間所發生的視覺變化。

為什麼需要頁面轉場 Page Transition?

當使用者從一個頁面前往另一個頁面時,如果畫面突然硬切、出現短暫白屏、載入過慢或讓使用者失去原本的操作脈絡,都可能讓人感到中斷和困惑,甚至提高離開的機率。

例如我用我自己的網站當作範例,原生網站在切換頁面時,網頁會直接跳轉,造成使用者體驗上的割裂感。

而頁面轉場 Page Transition 的目的就是要降低這種斷裂感,只要頁面之間的過渡做得好,就可以保留使用者的注意力、提供視覺的連續性和正面回饋來增強使用者的體驗,同時也能讓網頁更美觀和有趣來加強品牌形象。

如何使用 Page Transition

由於這個專案是使用 Next.js 開發,所以也會使用 Next.js 當作範例,一步一步帶你做出這種 Page Transition 的效果。

使用 Page Transition 有 2 種方法:

  1. 使用 View Transition API
  2. 使用 JavaScript 做頁面切換

View Transition API 是瀏覽器支援的,因此能做到更多種的效果,例如這個網站,就是把前後頁面的內容同時顯示在網頁上,並用推移的方式把舊的內容推走

但 View Transition API 的缺點是支援度還沒有很好,可以看到目前 FireFox 只有部分功能支援,一些比較舊的電腦也可能不知的這個功能

而我的專案,是 2 個都採用,在支援 View Transition API 的裝置使用 View Transition API,而如果不支援,就退回使用 JavaScript 做頁面切換。

筆記

若你想使用 View Transition API,可以參考 next-view-transitions 套件。 但如果想要避免支援度的問題,可以使用 next-transition-router 這個 library。

接著就來看看我是如何使用這 2 者的吧!

使用 next-transition-router

首先先處理 JS 版本的頁面轉換,如果是 Next.js 專案要使用 Page Transition,最快速的方式就是使用這個 Library:next-transition-router:

筆記

如果你使用原生 JavaScript,可以參考這篇文章:Different Page Transitions For Different Circumstances

1. 安裝 next-transition-router

輸入下面指令安裝:

bash
pnpm add next-transition-router

安裝後,我們新增一個 <TransitionRouterProvider/> 來使用套件提供的 <TransitionRouter />,他可以傳 auto prop 來自動監聽所有的 Next 連結:

tsx
'use client';

import { useRef } from 'react';
import { animate } from 'motion';
import { TransitionRouter } from 'next-transition-router';

export function TransitionRouterProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const wrapperRef = useRef<HTMLDivElement>(null!);

  return (
    <TransitionRouter
      auto
      leave={(next) => {
        animate(
          wrapperRef.current,
          { opacity: [1, 0] },
          { duration: 0.5, onComplete: next }
        );
      }}
      enter={(next) => {
        animate(
          wrapperRef.current,
          { opacity: [0, 1] },
          { duration: 0.5, onComplete: next }
        );
      }}
    >
      <div ref={wrapperRef}>{children}</div>
    </TransitionRouter>
  );
}

並且在 layout.tsx 中使用,基本就完成了:

tsx
<body>
  <TransitionRouterProvider>{children}</TransitionRouterProvider>
</body>

2. 增加更多 Page Transition 效果

如果你只需要淡入淡出的效果,這樣就已經做到了,但如果你想要再增添更多動畫,來增強網站的品牌感,可以參考我目前網站效果的思路:

  1. 離開時:頁面內容 opacity 1 → 0,並搭配 translateY 向上位移;深色 overlay 從畫面底部向上展開,覆蓋目前頁面。
  2. 進入時:路由切換後,overlay 移出畫面顯示新內容;新頁面內容 opacity 0 → 1,並由 translateY 位移狀態回到初始位置。

要實現這樣的 Page Transition,我們需要多一個元素來做遮罩,並且在 wrapperRef 增加 translate 的效果,

我們先新增一個元素遮罩,使用 clip-path:polygon(0%_100%,100%_100%,100%_100%,0%_100%) 來讓遮罩的初始樣式是隱藏的:

tsx
<div
  ref={overlayRef}
  className="fixed inset-0 top-0.75 z-router-overlay h-full w-full bg-primary [clip-path:polygon(0%_100%,100%_100%,100%_100%,0%_100%)]"
/>

因為要控制這個遮罩,所以也要使用一個 overlayRef 來抓取 DOM 元素

tsx
const overlayRef = useRef<HTMLDivElement>(null!);

接著處理 leave 和 enter 的邏輯:

tsx
<TransitionRouter
  auto
  leave={(next) => {
    animate([
      [
        overlayRef.current,
        {
          clipPath: leaveClipPath,
        },
        {
          duration: duration,
          ease: VIEW_TRANSITION_EASING,
          at: 0,
        },
      ],
      [
        wrapperRef.current,
        {
          opacity: [1, 0],
          y: leaveY,
        },
        {
          y: {
            duration: duration,
            ease: VIEW_TRANSITION_EASING,
          },
          opacity: {
            duration: duration,
            ease: 'linear',
          },
          at: 0,
        },
      ],
    ]).then(next);
  }}
  enter={(next) => {
    animate([
      [
        overlayRef.current,
        {
          clipPath: enterClipPath,
        },
        {
          duration: duration,
          ease: VIEW_TRANSITION_EASING,
          at: 0,
        },
      ],
      [
        wrapperRef.current,
        {
          opacity: [0, 1],
          y: enterY,
        },
        {
          y: {
            duration: duration,
            ease: VIEW_TRANSITION_EASING,
          },
          opacity: {
            duration: duration,
            ease: 'linear',
          },
          at: 0,
        },
      ],
    ]).then(next);
  }}
>
  <div
    ref={overlayRef}
    className="fixed inset-0 top-0.75 z-router-overlay h-full w-full bg-primary [clip-path:polygon(0%_100%,100%_100%,100%_100%,0%_100%)]"
  />
  <div ref={wrapperRef}>{children}</div>
</TransitionRouter>

最後,我使用 useReducedMotion 在希望不要太多動畫的裝置上,減少 Page Transition 的效果,例如取消 Overlay 和位移,以下是完整的程式碼:

tsx
const VIEW_TRANSITION_EASING = [0.9, 0, 0.1, 1] as const;

export function TransitionRouterProvider({
  children,
  footerData,
}: {
  children: React.ReactNode;
  footerData: FooterData;
}) {
  const overlayRef = useRef<HTMLDivElement>(null!);
  const wrapperRef = useRef<HTMLDivElement>(null!);

  const shouldReduceMotion = useReducedMotion();
  const leaveClipPath = shouldReduceMotion
    ? null
    : [
        'polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)',
        'polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%)',
      ];
  const enterClipPath = shouldReduceMotion
    ? null
    : [
        'polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%)',
        'polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)',
      ];
  const leaveY = shouldReduceMotion ? 0 : [0, -240];
  const enterY = shouldReduceMotion ? 0 : [240, 0];

  const duration = shouldReduceMotion ? 0.4 : 0.8;

  return (
    <TransitionRouter
      auto
      leave={(next) => {
        animate([
          [
            overlayRef.current,
            {
              clipPath: leaveClipPath,
            },
            {
              duration: duration,
              ease: VIEW_TRANSITION_EASING,
              at: 0,
            },
          ],
          [
            wrapperRef.current,
            {
              opacity: [1, 0],
              y: leaveY,
            },
            {
              y: {
                duration: duration,
                ease: VIEW_TRANSITION_EASING,
              },
              opacity: {
                duration: duration,
                ease: 'linear',
              },
              at: 0,
            },
          ],
        ]).then(next);
      }}
      enter={(next) => {
        animate([
          [
            overlayRef.current,
            {
              clipPath: enterClipPath,
            },
            {
              duration: duration,
              ease: VIEW_TRANSITION_EASING,
              at: 0,
            },
          ],
          [
            wrapperRef.current,
            {
              opacity: [0, 1],
              y: enterY,
            },
            {
              y: {
                duration: duration,
                ease: VIEW_TRANSITION_EASING,
              },
              opacity: {
                duration: duration,
                ease: 'linear',
              },
              at: 0,
            },
          ],
        ]).then(next);
      }}
    >
      <div
        ref={overlayRef}
        className="fixed inset-0 top-0.75 z-router-overlay h-full w-full bg-primary [clip-path:polygon(0%_100%,100%_100%,100%_100%,0%_100%)]"
      />

      <div ref={wrapperRef}>{children}</div>
    </TransitionRouter>
  );
}

使用 next-view-transitions 做加強

前面使用 next-transition-router 時,頁面會先播放離場動畫,等路由完成後再播放入場動畫,這種方式已經可以處理大部分的頁面切換,但若要像前面所說,想要同時保留新舊頁面,再用推移、淡入淡出等效果處理兩個畫面,就必須使用 View Transition API,如下影片:

我們可以使用 next-view-transitions 這個套件,他把瀏覽器原生的 View Transition API 整合到 Next.js App Router 中。

next-view-transitions 安裝與基本使用

先安裝套件:

bash
pnpm add next-view-transitions

接著在專案的根 layout 使用 ViewTransitions 包住整個網站:

tsx
import { ViewTransitions } from 'next-view-transitions';

export default function Layout({ children }) {
  return <ViewTransitions>{children}</ViewTransitions>;
}

如果要套用 View Transition 的頁面轉場,並須改用套件提供的 Link:

tsx
import { Link } from 'next-view-transitions';

<Link href="/about">About</Link>;

套件會在連結點擊時呼叫 document.startViewTransition(),等待 Next.js 完成畫面更新後,再讓瀏覽器播放轉場。

瀏覽器的上一頁與下一頁操作,也會透過 popstate 走相同流程。

接著我們要新增一個 css file,因為 View Transition API 會建立一組 pseudo-element,我們可以在 CSS 裡控制舊畫面和新畫面的動畫:

css
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
}

::view-transition-old(page-content) {
  animation: page-content-leave 1000ms ease-in-out both;
}

::view-transition-new(page-content) {
  animation: page-content-enter 1000ms ease-in-out both;
}

@keyframes page-content-leave {
  from {
    transform: translateY(0);
    opacity: 1;
  }

  to {
    transform: translateY(-35%);
    opacity: 0.2;
  }
}

@keyframes page-content-enter {
  from {
    clip-path: polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%);
  }

  to {
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
  }
}

如果要替特定區塊命名,則在前後畫面使用相同的 view-transition-name:

css
.view-transition-page {
  view-transition-name: page-content;
}

這樣只會針對 page-content 播放動畫,也能避免 root 的預設淡入淡出同時播放,這樣基本上就大功告成啦:

如何兼具兩個套件?

我的做法是讓兩個套件分工,當瀏覽器支援 View Transition API 時,使用 next-view-transitions 的原生轉場;不支援時,退回 next-transition-router 的 JavaScript 動畫。

做法是透過 "startViewTransition" in document 來判斷是否支援 Transition API,並且搭配 next-transition-router 的 auto props,他會在 true 時自動替換掉網站的連結,使連結支援 Page Transition,所以如果瀏覽器支援 View Transition API,就要把它關掉:

tsx
const [isViewTransitionSupport, setIsViewTransitionSupport] = useState(false);

useEffect(() => {
  setIsViewTransitionSupport('startViewTransition' in document);
}, []);

<TransitionRouter auto={isViewTransitionSupport === false}>
  {children}
</TransitionRouter>;

另外我的專案是使用 CustomLink 元件,讓我能依照連結類型有不同的功能,比如跨頁連結使用 ViewTransitionLink,hash 連結、外部連結和目前所在頁面則使用一般的 NextLink:

tsx
const hrefPathname =
  typeof href === 'string'
    ? href.split('#')[0]?.split('?')[0]
    : href?.pathname?.split('#')[0]?.split('?')[0];

const isSamePath = hrefPathname === pathname;
const isHashLink = typeof href === 'string' && href.startsWith('#');
const LinkComponent =
  isExternalHref(href) || isHashLink || isSamePath
    ? NextLink
    : ViewTransitionLink;

這樣在點擊指向目前頁面的首頁連結時,舊不會重新播放頁面轉場;文章目錄也只會更新 hash 和 scroll 位置。

Next.js 的 scroll 問題

接上 View Transition API 後,我遇到另一個 scroll 問題。

Next.js 會依照自己的導航流程處理 window 的 scroll,但轉場期間舊頁面和新頁面會同時存在,在切換頁面時,會看到舊頁面觸發了 Next 的 scroll 自動滾到最上面,畫面會明顯跳動:

我的解法是在路由內容外再包一層 wrapper,並限制它的高度搭配 overflow-y-hidden,因為 Next 的 Scroll 會啟用在 window scroll 上,但如果 scroll 的位置不是 window 而是一般的 <div> 就可避免這個問題:

tsx
<div id="site-scroll-container" className="max-h-screen overflow-y-hidden">
  <div className="view-transition-page">{children}</div>
</div>

瀏覽器上一頁和下一頁的 hash 問題

不過後來我用遇到另一個問題,我的文章目錄是使用 #heading-2 這種 hash 連結,點擊目錄時,URL 和畫面都會正常更新,但按瀏覽器上一頁或下一頁時,next-view-transitions 會把 hash 的變化當成頁面切換,因此當用戶點擊文章目錄時也會播放完整的頁面轉場。

後來去看 next-view-transition 的程式碼後發現,套件會對每次 popstate 呼叫 document.startViewTransition(),但 popstate 不只會在不同頁面之間觸發,使用者在同一頁切換 hash 時也會觸發。

所以我利用 pnpm patch 在套件的 onPopState 補上 pathname 判斷:

tsx
const onPopState = () => {
  if (window.location.pathname === currentPathname.current) {
    return;
  }

  // 其他程式碼 ...
};

如此一來,從 /article#heading-2 到 /article#heading-3 時,只會跳到對應段落,不會播放頁面轉場。

接著,還要在 hashchange 事件裡手動處理文章目錄的 scroll 功能:

tsx
function handleHashChange() {
  const nextHashId = getHashId();

  if (!nextHashId) {
    document.querySelector('main')?.scrollIntoView({
      behavior: 'auto',
      block: 'start',
    });
    return;
  }

  requestAnimationFrame(() => {
    document.getElementById(nextHashId)?.scrollIntoView({
      behavior: 'auto',
      block: 'start',
    });
  });
}

當網址沒有沒有 hash 的情況,比如從 ``/article#heading-2按上一頁回到/article 時,window.location.hash` 會變成空字串,此時就要回到畫面頂端。

筆記

第三方套件需要修改時,使用 pnpm patch 保存變更,避免直接改動 node_modules:

bash
pnpm patch next-view-transitions@0.3.5
# 修改暫存目錄裡的 dist/index.js
pnpm patch-commit <暫存目錄>/node_modules/next-view-transitions
pnpm install

接著執行 pnpm patch-commit 後,repo 會留下 patches/next-view-transitions@0.3.5.patch,也就是我們修改後的檔案。

使用 Page Transition 要注意的事情

雖然 Page Transition 可以讓網站的切換更有記憶點,也能讓新舊頁面之間的關係更清楚。

不過,它本質上仍然是一段的動畫,如果時間太長、效果太多,或每次切換都過度強調,反而會讓使用者覺得網站變慢,甚至造成操作上的干擾。

以下是幾個在使用 Page Transition 時需要注意的重點。

1. 時間不要太長

大多數 UI 動畫的時間建議落在 100ms 到 500ms 之間,實際長度會取決於動畫的複雜度與元素移動距離。動畫太快,使用者可能看不清楚狀態變化;但動畫太慢,則會讓人覺得被迫等待。

Page Transition 屬於比較大範圍的畫面切換,因此可以比一般 micro interaction 稍微長一些,不過我會建議整體體感時間控制在 500ms 到 1600ms 左右;如果超過 1600ms,使用者就會開始感覺到延遲。

2. 動畫要有方向感

動畫的目的除了裝飾以外,重點應該是要幫助使用者理解狀態、路徑與內容關係,而不只是讓畫面看起來比較炫。所以 page transition 要注意:

  1. 從哪裡離開
  2. 從哪裡進入
  3. 新舊頁面之間的層級關係和方向關係

這也是我在網站中讓舊頁面往上離開、新頁面由下往上進入的原因。這樣的動線會讓切換更像是往下一個內容段落前進的感覺,而不是單純把兩個頁面硬接在一起。

3. 需要注意效能

Page Transition 通常會影響整個畫面,所以效能比一般小型互動更重要。

建議在動畫上,優先考慮 opacity、transform 等效果,如果要加上 blur 、clippath、 box-shadow 或 width、 height 等 layout 變化,就需要謹慎一點,因為可能讓在切換頁面時,出現卡頓的效果。

4. 支援 reduced motion

prefers-reduced-motion 可以偵測使用者是否在系統層級要求減少動畫。對於不想要動畫的使用者,應該減少或避免不必要的動畫。

我的做法是減少動畫效果,而沒有完全取消。

next-transition-router 的實現邏輯

到這邊,我們已經完成了 2 種方式的 Page Transition 了,但我很好奇這個 next-transition-router 是如何實現這樣的效果的,所以我去看了他的原始程式碼,結果發現實現的方式也不難,我們自己手寫一個也不需要花太多時間。

這個套件核心只有 8 KB 左右,本質上是透過**控制反轉(Inversion of Control)**與 React Hook 生命週期清理機制,來達到以下流程:

  1. 離場動畫
  2. 路由更新
  3. 入場動畫

第一步:攔截導航行為

要實現轉場動畫,第一步要先攔截所有觸發頁面跳轉的行為,阻止瀏覽器與 Next.js 預設立刻換頁的動作。

這個庫提供了自定義的 <Link> 元件來手動攔截。但如果專案規模很大,我們不想一個一個去替換原生的 Next.js Link,最方便的做法是直接使用 Provider 的 auto={true} 功能。

auto 模式:全域事件代理

因為 Next.js 的 <Link> 最終在 DOM 中都是帶有 href 的 <a> 標籤,

所以開啟 auto 後,Provider 會用 delegate-it 在 document 全域監聽所有 a[href] 的 click 事件,符合條件就用 event.preventDefault() 阻止原生換頁並接管導航,不需要手動替換每個 Link 的 import。

tsx
useEffect(() => {
  if (!auto) return;
  const controller = new AbortController();
  delegate('a[href]', 'click', handleClick, { signal: controller.signal });
  return () => controller.abort();
}, [auto, handleClick]);

為什麼要用 delegate-it?

delegate-it 解決的是**事件委派(event delegation)**的問題。

直接自己監聽的話,你會這樣寫:

javascript
document.querySelectorAll('a[href]').forEach((link) => {
  link.addEventListener('click', handleClick);
});

這有兩個問題:

  1. 抓不到之後才渲染的連結:Next.js 換頁後,新頁面的 <a> 是之後才掛進 DOM 的,之前的 querySelectorAll 不會包含它們,所以新連結點了沒有轉場效果。
  2. 要自己處理 cleanup:每個 listener 都要記得移除,N 個連結就要移除 N 次。

delegate-it 的做法是把單一個 listener 掛在 document 上,利用事件冒泡攔截所有 a[href] 的點擊:

javascript
delegate('a[href]', 'click', handleClick, { signal: controller.signal });

不管連結是什麼時候渲染的,點擊事件都會冒泡到 document 被攔截到。cleanup 也只需要 controller.abort() 一次搞定,非常簡單直接。

安全過濾:shouldLinkTriggerTransition

但我們不能盲目攔截所有連結。

如果使用者按住 Command 或 Ctrl 鍵(想在新分頁開啟)、連結標記為 target="_blank" 或 download,或是連到外部網站,都應該讓瀏覽器執行原生動作。

所以這個套件會使用 shouldLinkTriggerTransition 先做判斷

typescript
export function shouldLinkTriggerTransition(
  link: HTMLAnchorElement,
  event: any
): boolean {
  return (
    link.target !== '_blank' &&
    link.origin === window.location.origin &&
    link.rel !== 'external' &&
    !link.download &&
    !isModifiedEvent(event) &&
    !event.defaultPrevented
  );
}

useTransitionRouter:重構程式化導航

除了點擊連結外,使用者也可能用 router.push('/about') 來切換路由。

所以如果要使用這種方式,必須使用套件提供的 useTransitionRouter ,他會重寫原生的 push 與 replace,統一路由的行為。

tsx
export function useTransitionRouter() {
  const router = useRouter();
  const pathname = usePathname();
  const { navigate } = useTransitionState();

  const push = useCallback(
    (href: string, options?: NavigateOptions) => {
      navigate(href, pathname, 'push', options);
    },
    [pathname, navigate]
  );
  // ...
}

第二步:狀態機與動畫生命週期核心

攔截完導航後,需要一個全域狀態機來協調「離場動畫 → 路由跳轉 → 入場動畫」的順序。

這個套件設計了三個狀態:"none"(靜止)、"leaving"(離場中)、"entering"(入場中)。

navigate:延遲路由跳轉,呼叫離場動畫

當 navigate 被呼叫時,並不會直接換頁,而是把真正的路由切換包成 next() 回調,傳給開發者定義的 leave。

typescript
const next = () => router[method](href, options);
// ...
setStage('leaving');
leaveRef.current = await leave(next, pathname, href);

頁面狀態設為 "leaving" 後,離場動畫開始播放;動畫結束時,開發者手動呼叫 next(),Next.js 才真正換頁。

React effect cleanup:自動偵測路由完成

這是整個套件最精妙的設計在於 pathname 改變(代表 Next.js 換頁完成)時,React 在執行新 effect 前,會先執行上一次 effect 的 cleanup。

這個 cleanup 讀到的舊 stage 是 "leaving",就在這個「新頁面 DOM 掛載、舊 effect 卸載」的瞬間,自動將狀態切換到 "entering"。

typescript
useEffect(() => {
  return () => {
    if (stage === 'leaving') {
      setStage('entering');
    }
  };
}, [stage, pathname]);

入場動畫與狀態歸零

stage 切換到 "entering" 時,會觸發一個 effect 執行開發者傳入的 enter 入場動畫。

傳給 enter 的參數是一個把 stage 重設為 "none" 的回調,當動畫結束呼叫它,狀態完整歸零。

typescript
useEffect(() => {
  if (stage === 'entering') {
    const runEnter = async () => {
      enterRef.current = await Promise.resolve(enter(() => setStage('none')));
    };
    runEnter();
  }
}, [stage, enter]);

總結

Page Transition 做得好,對於使用者來說是大大的加分,而實現方式其實也不會太過困難,使用 next-transition-router 就能做到。

next-transition-router 的本身其實不複雜,核心就是兩件事:

  1. 攔截導航,把 router.push 包成 next() 傳給使用者,讓動畫決定換頁時機
  2. 靠 useEffect cleanup 偵測換頁完成,在 pathname 改變的瞬間自動切換狀態,銜接入場動畫

完整流程圖

用流程圖來表示就是:

  1. 使用 delegate-it 監聽 Link 點擊事件(如果使用 auto={true})
  2. 使用者點擊連結或呼叫 router.push() 觸發 navigate() 函式
  3. 符合轉場條件?(同源、非 hash、非同頁)
  4. setStage("leaving"),呼叫 leave(next, from, to) 函式
  5. 播放離場動畫,動畫結束,開發者呼叫 next()
  6. router.push() 執行,Next.js 開始換頁
  7. pathname 改變
  8. useEffect([stage, pathname]) cleanup 執行,舊 stage === "leaving" → setStage("entering")
  9. useEffect([stage, enter]) 觸發,呼叫 enter(() => setStage("none"))
  10. 播放入場動畫
  11. 動畫結束,開發者呼叫回調 next() 執行 setStage("none")

整個流程不依賴任何動畫庫,只借助 React 本身的生命週期就可以做到離場 → 換頁 → 入的順序,非常值得學習~!

文章參考

  • https://github.com/ismamz/next-transition-router
  • https://github.com/shuding/next-view-transitions
  • https://frontendmasters.com/blog/different-page-transitions-for-different-circumstances/

下一篇看什麼?

01.

前端測試架構完整指南:單元測試、整合測試、E2E 測試與測試金字塔

02.

10 大設計網站提升你的審美!讓你的網站更有質感

03.

2026 前端框架怎麼選?React、Vue、Angular 完整比較指南

文章目錄

  1. 為什麼需要頁面轉場 Page Transition?
  2. 如何使用 Page Transition
  3. 使用 next-transition-router
  4. 1. 安裝 next-transition-router
  5. 2. 增加更多 Page Transition 效果
  6. 使用 next-view-transitions 做加強
  7. next-view-transitions 安裝與基本使用
  8. 如何兼具兩個套件?
  9. Next.js 的 scroll 問題
  10. 瀏覽器上一頁和下一頁的 hash 問題
  11. 使用 Page Transition 要注意的事情
  12. 1. 時間不要太長
  13. 2. 動畫要有方向感
  14. 3. 需要注意效能
  15. 4. 支援 reduced motion
  16. next-transition-router 的實現邏輯
  17. 第一步:攔截導航行為
  18. 第二步:狀態機與動畫生命週期核心
  19. 總結
  20. 完整流程圖

訂閱電子報!

和 2000+ 人一起學習 AI、軟體與網站實作資訊。

或來信合作:kun@thisweb.dev

頁面導覽

  • 首頁
  • 所有文章

聯絡資訊

THISWEB