コンテンツにスキップ

ビュートランジション

Astroでは、オプトイン式ページ単位のビュートランジションを数行のコードで実現できます。ビュートランジションは、ブラウザがナビゲーション時に通常おこなうページ全体の更新なしでページコンテンツを更新し、ページ間のシームレスなアニメーションを提供します。

Astroは、単一のページの<head>に追加可能な<ViewTransitions />ルーティングコンポーネントを提供しており、これにより別のページに移動する際のページ遷移を制御できます。このコンポーネントは軽量なクライアントサイドルーターを提供し、ナビゲーションをインターセプトしてページ間の遷移をカスタマイズできるようにします。

このコンポーネントを共通のヘッドやレイアウトなど再利用可能な.astroコンポーネントに追加すると、サイト全体でのアニメーション付きのページ遷移(SPAモード)が可能になります。

Astroのビュートランジションは新しいView TransitionsブラウザAPIによって提供されており、また以下の機能も含みます。

ViewTransitions />ルーティングコンポーネントをインポートし、<head>内に追加することで、ビュートランジションを個別のページで有効化できます。

src/pages/index.astro
---
import { ViewTransitions } from 'astro:transitions';
---
<html lang="ja">
<head>
<title>私のホームページ</title>
<ViewTransitions />
</head>
<body>
<h1>私のウェブサイトへようこそ!</h1>
</body>
</html>

サイト全体でのビュートランジション(SPAモード)

セクションタイトル: サイト全体でのビュートランジション(SPAモード)

<ViewTransitions />をインポートし、共通の<head>または共有のレイアウトコンポーネントに追加します。Astroは、旧ページと新ページの類似点に基づいてデフォルトのページアニメーションを作成し、サポートされていないブラウザに対するフォールバック動作も提供します。

以下の例では、このコンポーネントをインポートし<CommonHead /> Astroコンポーネントに追加することで、Astroのデフォルトのページナビゲーションアニメーションをサイト全体に追加しています。これにより、サポートされていないブラウザに対するフォールバック動作も追加されます。

components/CommonHead.astro
---
import { ViewTransitions } from 'astro:transitions';
---
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<!-- 主要メタタグ -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<ViewTransitions />

Astroのデフォルトのクライアントサイドナビゲーションを有効にするために、他に必要な設定はありません!

より細かな制御のためには、トランジションディレクティブを使用するか、個々の要素でデフォルトのクライアントサイドナビゲーションをオーバーライドします。

Astroは、旧ページと新ページの両方に存在する対応する要素に、共通かつ一意のview-transition-nameを自動的に割り当てます。マッチする要素のペアは、要素の種類とDOM内の位置の両方によって推測されます。

.astroコンポーネント内のページ要素にオプションのtransition:*ディレクティブを使用すると、ナビゲーション中のページ遷移の動作をより細かく制御できます。

場合によっては、対応するビュートランジション要素を自分で特定したい、または特定する必要があるかもしれません。transition:nameディレクティブを使用して、要素のペアに名前を付けることができます。

old-page.astro
<aside transition:name="hero">
new-page.astro
<aside transition:name="hero">

transition:nameに使った値は、各ページにつき1回しか使用できないことに注意してください。Astroが適切な名前を自動的に推測できない場合や、要素のマッチングをより細かく制御したい場合に、手動で設定してください。

追加: astro@2.10.0

transition:persistディレクティブを使用すると、ページ間のナビゲーションでコンポーネントとHTML要素を(置き換えるのではなく)保持できます。

たとえば以下の<video>は、同じvideo要素を含む別のページに移動しても再生され続けます。これは、進む・戻るの両方のナビゲーションで機能します。

components/Video.astro
<video controls="" autoplay="" transition:persist>
<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
</video>

Astroアイランドclient:ディレクティブをもつUIフレームワークコンポーネント)にもディレクティブを配置できます。そのコンポーネントが次のページに存在する場合、旧ページのアイランドは現在の状態のまま表示され、新しいページのアイランドに置き換えられることはありません。

以下の例では、transition:persist属性をもつ<Counter />コンポーネントを含むページ間を行き来しても、countはリセットされません。

components/Header.astro
<Counter client:load transition:persist count={5} />

また、アイランドあるいは要素が2つのページ間で異なるコンポーネントの中にある場合は、対応する要素を手動で特定できます。

pages/old-page.astro
<Video controls="" autoplay="" transition:name="media-player" transition:persist />
pages/new-page.astro
<MyVideo controls="" autoplay="" transition:name="media-player" transition:persist />

便利な短縮記法として、transition:persistの値にトランジション名を指定できます。

pages/index.astro
<video controls="" autoplay="" transition:persist="media-player">

Astroには、デフォルトのfadeトランジションをオーバーライドするための組み込みのアニメーションがいくつか用意されています。transition:animateディレクティブを各要素に追加して、特定のトランジションの動作をカスタマイズできます。

  • fade(デフォルト): 独自のクロスフェードアニメーションです。旧コンテンツがフェードアウトし、新コンテンツがフェードインします。
  • initial: Astroの独自のクロスフェードアニメーションを無効にし、ブラウザのデフォルトのスタイルを使用します。
  • slide: 旧コンテンツが左にスライドアウトし、新コンテンツが右からスライドインするアニメーションです。戻る場合のナビゲーションでは、アニメーションは逆になります。
  • fade: 旧コンテンツがフェードアウトし、新コンテンツがフェードインするクロスフェードです。
  • none: ブラウザのデフォルトのアニメーションを無効にします。ページの<html>要素で使用すると、ページ内のすべての要素のデフォルトのフェードを無効にします。

ページのアニメーションを完全に制御するには、ディレクティブを組み合わせます。<html>要素でページのデフォルト値を設定し、必要に応じて個々の要素でオーバーライドしてください。

以下の例では、ボディコンテンツにスライドアニメーションを設定し、ページの残りの部分に対してはブラウザのデフォルトのフェードアニメーションを無効にしています。

---
import { CommonHead } from '../components/CommonHead.astro';
---
<html transition:animate="none">
<head>
<CommonHead />
</head>
<body>
<header>
...
</header>
<!-- 個別の要素でページデフォルトをオーバーライドします -->
<main transition:animate="slide">
...
</main>
</body>
</html>

任意のCSSアニメーションプロパティを使用して、トランジションのすべての要素をカスタマイズできます。

組み込みのアニメーションをカスタマイズするには、まずastro:transitionsからアニメーションをインポートし、続いてカスタマイズ用オプションを渡します。

以下の例では、組み込みのfadeアニメーションの継続時間(duration)をカスタマイズしています。

---
import { fade } from 'astro:transitions';
---
<header transition:animate={fade({ duration: '0.4s' })}>

また、transition:animateで独自のアニメーションも定義できます。進む・戻るの挙動と新旧のページについて、以下の型に従って定義します。

export interface TransitionAnimation {
name: string; // キーフレームの名前
delay?: number | string;
duration?: number | string;
easing?: string;
fillMode?: string;
direction?: string;
}
export interface TransitionAnimationPair {
old: TransitionAnimation | TransitionAnimation[];
new: TransitionAnimation | TransitionAnimation[];
}
export interface TransitionDirectionalAnimations {
forwards: TransitionAnimationPair;
backwards: TransitionAnimationPair;
}

以下の例は、独自のfadeアニメーションを定義するために必要なすべてのプロパティを示しています。

---
const anim = {
old: {
name: 'fadeIn',
duration: '0.2s',
easing: 'linear',
fillMode: 'forwards',
},
new: {
name: 'fadeOut',
duration: '0.3s',
easing: 'linear',
fillMode: 'backwards',
}
};
const myFade = {
forwards: anim,
backwards: anim,
};
---
<header transition:animate={myFade}> ... </header>

<ViewTransitions />コンポーネントを追加してページでクライアントサイドルーティングを有効にすると、サイト内の別のページにリンクするページ内のすべてのアンカーは、クライアントサイドルーティングによってナビゲートされます。ただ、クライアントサイドルーティングでナビゲートしたくないような場合もあります。たとえば、public/フォルダ内のPDFや、画像を生成するAPIルートなどの非ページへのリンクです。

こうした場合、data-astro-reload属性を使用して、リンクごとにクライアントサイドルーティングをオプトアウトできます。

<a href="/quarterly-earnings.pdf" data-astro-reload>

これらのリンクはルーターによって無視され、フルページのナビゲーションが発生します。

<ViewTransitions />ルーターは、ビュートランジションをサポートするブラウザ(すなわちChromiumブラウザ)で最も有効に機能しますが、他のブラウザに対するデフォルトのフォールバックサポートも含んでいます。ブラウザがView Transitions APIをサポートしていなくても体験を損なわないように、Astroはフォールバックオプションのいずれかを使用してブラウザ内でのナビゲーションを提供します。

<ViewTransitions />コンポーネントにfallbackプロパティを追加し、swapまたはnoneに設定することで、Astroのデフォルトのフォールバックサポートをオーバーライドできます。

  • animate (デフォルト、推奨) - Astroは、ページコンテンツを更新する前に、カスタム属性を使用してビュートランジションをシミュレートします。
  • swap - Astroはページのアニメーションを試みません。代わりに、旧ページはすぐに新しいページに置き換えられます。
  • none - Astroはアニメーションによるページ遷移を一切おこないません。代わりに、サポート外のブラウザではフルページのナビゲーションがおこなわれます。
---
import { ViewTransitions } from 'astro:transitions';
---
<title>私のサイト</title>
<ViewTransitions fallback="swap">

<ViewTransitions />ルーターを使用すると、以下の流れでAstroのクライアントサイドナビゲーションが実行されます。

  1. サイト訪問者が以下のいずれかのアクションを実行し、ナビゲーションがトリガーされる。

    • サイト内の別のページへ内部リンクする<a>タグをクリックする。
    • 戻るボタンをクリックする。
    • 進むボタンをクリックする。
  2. ルーターが次のページの取得を開始する。

  3. ルーターが、'forward'または'back'の値をもつdata-astro-transition属性をHTML要素に追加する。

  4. ルーターがdocument.startViewTransitionをコールする。これにより、ブラウザのview transition processがトリガーされる。重要なこととして、ブラウザはページの現在の状態をスクリーンショットする。

  5. startViewTransitionコールバック内で、ルーターがswapを実行する。これは以下の一連のイベントから構成される。

    • <head>のコンテンツが、いくつかの要素を残して入れ替わる。

      • FOUCを防止するために、スタイルシートのDOMノードが新しいページに存在する場合は残される。
      • 新しいページにスクリプトが存在する場合は残される。
      • 新しいページに対応する要素がある場合、transition:persistをもつ他のhead要素は残される。
    • <body>は、新しいページのbodyに完全に置き換えられる。

    • transition:persistとマークされた要素が新しいページに存在する場合、新しいDOMに移動する。

    • 必要に応じてスクロール位置が復元される。

    • astro:after-swapイベントがdocumentでトリガーされる。これがswapプロセスの終わりとなる。

  6. ルーターが、トランジションを解決する前に新しいスタイルシートのロードを待つ。

  7. ルーターが、ページに追加された新しいスクリプトを実行する。

  8. astro:page-loadイベントが発火する。これがナビゲーションプロセスの終わりとなる。

<ViewTransitions />コンポーネントを使用してページ間をナビゲートする際、ブラウザの動作に合わせてスクリプトが順に実行されます。

グローバルな状態を設定しているコードがある場合は、そのスクリプトが複数回実行される可能性があることを考慮する必要があります。<script>タグでグローバルな状態をチェックし、可能な限り条件付きでコードを実行してください。

<script is:inline>
if(!window.SomeGlobal) {
window.SomeGlobal = {} // ....
}
</script>

モジュールスクリプトは、ブラウザがロード済みのモジュールを追跡しているため、常に1回だけ実行されます。これらのスクリプトでは再実行の心配は必要ありません。

新しいページがユーザーに表示され、ブロッキングスタイルとスクリプトがロードされたあとの、ページナビゲーションの最後に発生するイベントです。documentでこのイベントをリッスンできます。

<ViewTransitions />コンポーネントは、初回のページナビゲーション(MPA)と、その後の進む・戻るのナビゲーションの両方でこのイベントを発生させます。

このイベントを使用すると、すべてのページナビゲーションでコードを実行したり、あるいは1度だけ実行したりできます。

<script>
document.addEventListener('astro:page-load', () => {
// これは1度だけ実行されます。
setupStuff();
}, { once: true });
</script>

旧ページが新ページに置き換わった直後に発生するイベントです。documentでこのイベントをリッスンできます。

このイベントは、新しいページに引き継ぐ必要があるDOM上の状態を復元するのに役立ちます。

たとえばダークモードのサポートを実装している場合、このイベントを使用してページロード間で状態を復元できます。

<script>
const setDarkMode = () => {
if (localStorage.darkMode) {
document.documentElement.dataset.dark = '';
}
};
// 初回のナビゲーションで実行される
setDarkMode();
// ビュートランジションのナビゲーションで実行される
document.addEventListener('astro:after-swap', setDarkMode);
</script>

Astroの<ViewTransitions />コンポーネントにはCSSメディアクエリが含まれており、prefer-reduced-motionの設定が検出された場合、フォールバックアニメーションを含むすべてのビュートランジションアニメーションが無効になります。代わりに、ブラウザはアニメーションなしでDOM要素を単純に入れ替えます。

Astro v3.0では、ビュートランジションを使うために実験的なフラグは不要となりました。

Astro 2.xでこの実験的なフラグを有効にしていなかった場合、プロジェクトへの破壊的な変更はありません。新しいビュートランジションAPIは、既存のコードに影響を与えません。

実験的なビュートランジションを使用していた場合、以前のバージョンからAstroプロジェクトをアップグレードする際に、いくつかの破壊的な変更が発生する可能性があります。

experimental.viewTransitions: trueが設定されたAstro v2.xプロジェクトをv3.0にアップグレードするためには、以下の手順に従ってください。

ビュートランジションの実験的なフラグを有効にしていた場合、Astro v3.0ではデフォルトでビュートランジションが有効になっているため、プロジェクトを更新する必要があります。

実験的なフラグを削除します。

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
viewTransitions: true
}
});

<ViewTransitions />コンポーネントはastro:componentsからastro:transitionsに移動しました。プロジェクト内のすべてのインポートソースを更新してください。

src/layouts/BaseLayout.astro
---
import { ViewTransitions } from "astro:components astro:transitions"
---
<html lang="ja">
<head>
<title>私のホームページ</title>
<ViewTransitions />
</head>
<body>
<h1>私のウェブサイトへようこそ!</h1>
</body>
</html>

変更: transition:animateの値morphinitialへとリネームされました。また、これはデフォルトのアニメーションではなくなりました。transition:animateディレクティブが指定されていない場合、アニメーションはデフォルトでfadeになります。

  1. morphアニメーションをinitialにリネームします。

    src/components/MyComponent.astro
    <div transition:name="name" transition:animate="morph initial" />
  2. デフォルトのmorphを使っていたアニメーションを維持するには、transition:animate="initial"を明示的に追加します。

    src/components/MyComponent.astro
    <div transition:name="name" transition:animate="initial" />
  3. 明示的にfadeに設定されていたアニメーションは安全に削除できます。これは現在デフォルトの動作となりました。

    src/components/MyComponent.astro
    <div transition:name="name" transition:animate="fade" />

追加: Astroはtransition:animateの値として新たにnoneをサポートしました。この値をページの<html>要素に使用すると、ページ全体のアニメーションを無効にできます。これにより、アニメーションディレクティブをもたないページ要素のデフォルトのアニメーションがオーバーライドされます。個々の要素にアニメーションを設定することはでき、指定したアニメーションが実行されます。

  1. 個々のページでデフォルトのトランジションをすべて無効にし、transition:animateディレクティブを明示的に使用している要素のアニメーションだけを有効化できます。

    <html transition:animate="none">
    <head></head>
    <body>
    <h1>Hello world!</h1>
    </body>
    </html>

astro:loadイベントはastro:page-loadにリネームされました。プロジェクト内でこのイベント名を使っているすべての箇所をリネームしてください。

src/components/MyComponent.astro
<script>
document.addEventListener('astro:load astro:page-load', runSetupLogic);
</script>

astro:beforeloadイベントはastro:after-swapにリネームされました。プロジェクト内でこのイベント名を使っているすべての箇所をリネームしてください。

src/components/MyComponent.astro
<script>
document.addEventListener('astro:beforeload astro:after-swap', setDarkMode);
</script>