本文書では Astro のバージョンが 2 から 3 へのバージョンアップにより新たに追加された新機能の中で特に重要な”View Transitions”とはどんな機能なのかについてシンプルなコードを利用して動作確認を行っています。

Astro は MPA(Multi Page Application)としてページ間を移動する度にページ全体の再読み込みが行われます。新機能の View Transitions 機能 を利用することでページを移動した時にページ全体を再読み込むすることなくスムーズに移動することができるようになります。ページ移動にはブラウザがNativeで持つView Transitions Browser APIを利用しています。設定も非常にシンプルでたった 2 行のコードを追加するだけで設定が完了します。 同じ技術を利用しているわけではありませんが Next.js, Nuxt, SvelteKit を利用した場合のページ移動を思い浮かべてもらえれば View Transitions がどんなものかわかるかと思います。

本文書では主に動作確認は Chrome ブラウザを利用して行っています(Firefox ブラウザも一部の動作確認で利用)。View Transitions Browser API はすべてのブラウザに対応している機能ではないので対応していないブラウザに対しては Polyfill を利用して対応させています。
マイナーのバージョンアップをする度にView Transitionsに関する機能の強化が行われているので一部内容が古くなっている可能性があります。

環境の構築

動作確認を行う Astro のプロジェクトを”npm create astro@latest”コマンドを実行して作成します。任意の名前をつけることができますが、プロジェクト名はコマンド後に自動で表示されるランダム名をそのまま利用しています。ここでは”wandering-wind”という名前です。プロジェクトのテンプレートの選択は “Include sample files” を選択します。TypeScript などの選択ははデフォルトを選択します。


 % npm create astro@latest
Need to install the following packages:
  create-astro@4.0.1
Ok to proceed? (y) y

╭─────╮  Houston:
│ ◠ ◡ ◠  Let's build something awesome!
╰─────╯

 astro   v3.0.8 Launch sequence initiated.

   dir   Where should we create your new project?
         ./wandering-wind

  tmpl   How would you like to start your new project?
         Include sample files
      ✔  Template copied

  deps   Install dependencies?
         Yes
      ✔  Dependencies installed

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict
      ✔  TypeScript customized

   git   Initialize a new git repository?
         Yes
      ✔  Git initialized

  next   Liftoff confirmed. Explore your project!

         Enter your project directory using cd ./wandering-wind
         Run npm run dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat

╭─────╮  Houston:
│ ◠ ◡ ◠  Good luck out there, astronaut! 🚀
╰─────╯

View Transitions

プロジェクトの作成直後のテンプレートファイルの中では View Transitions の設定は行われていないので View Transitions の設定を行う必要があります。

ページの移動設定

ページ間を移動するために必要となるリンクの設定をレイアウトファイルである Layout.astro ファイルで行います。a タグを利用して”/“と”/about”のページ間で移動できるように設定を行います。


---
interface Props {
	title: string;
}

const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about">About</a></li>
		</ul>
		<slot />
	</body>
</html>

pages ディレクトリの index.astro を書き換えます。



---
import Layout from '../layouts/Layout.astro';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1>Hello Astro 3.0</h1>
	</main>
</Layout>

“npm run dev”コマンドを実行して開発サーバを起動してブラウザから localhost:4321 にアクセスします。


 % npm run dev

> wandering-wind@0.0.1 dev
> astro dev

  🚀  astro  v3.0.8 started in 167ms

  ┃ Local    http://localhost:4321/
  ┃ Network  use --host to expose
index.astro
index.astro

/about のルーティングを設定するために pages ディレクトリに about.astro ファイルを作成して以下のコードを記述します。


---
import Layout from '../layouts/Layout.astro';
---

<Layout title="About Page">
	<main>
		<h1>About Astro</h1>
	</main>
</Layout>
Aboutページ
Aboutページ

View Transitions を設定していない状態なので、ページの上部に表示されているリンクをクリックしてページを移動するとページを移動するためにページが再読み込みされていることがブラウザのページタブのアイコンを見ることで確認できます。Astro ではデフォルトではページを移動する度にページの再読み込みが行われます。

初めての View Transitoions 設定

View Transitions を利用するために Layout.astro ファイルに’astro:transitions’から ViewTranstions を import して head タグの中に ViewTranstions タグを追加します。2 行を追加することで設定は完了です。


import { ViewTransitions } from 'astro:transitions';
interface Props {
	title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions />
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about">About</a></li>
		</ul>
		<slot />
	</body>
</html>

ページの上部にあるリンクをクリックしてください。先程までとは異なりブラウザのタブのアイコンが再読み込みされることもなくページの内容もスムーズに更新されていることが確認できます。リンクをクリックするだけではなくブラウザの back ボタンや forward ボタンをクリックしてもページ移動のアニメーションは動作します。さらに/about ページのリンクの上にマウスを乗せると prefetch が行われネットワークタブを見ると about ページの内容が取得されていることがわかります。

head タグに追加される内容

View Transitions の設定前と設定後ではどのような違いがあるのか確認していきます。

View Transitions 設定前の状態に戻し開発サーバを起動(npm run dev)して head タグの内容をブラウザのデベロッパーツールの要素で確認します。

設定前のheadタグの内容
設定前のheadタグの内容

script タグを確認することができますが HMR(Hot Module Replacement)と Vite に関するファイルで View Transitions に関するファイルではありません。

次に View Transitions 設定後の head タグの内容を確認します。

設定後のheadタグの内容
設定後のheadタグの内容

設定後には設定前に確認した script タグ以外に ViewTransitions.astro が script タグで指定されていることがわかります。

さらに開発環境だけではなく本番環境では head タグの内容がどのように変化するのか確認するために View Transitions 設定前の状態でビルドを実行(npm run build)してみましょう。


% npm run build

> wandering-wind@0.0.1 build
> astro build

22:15:04 [content] No content directory found. Skipping type generation.
22:15:04 [build] output target: static
22:15:04 [build] Collecting build info...
22:15:04 [build] Completed in 87ms.
22:15:04 [build] Building static entrypoints...
22:15:04 [build] Completed in 700ms.

 generating static routes
▶ src/pages/index.astro
  └─ /index.html (+22ms)
▶ src/pages/about.astro
  └─ /about/index.html (+3ms)
Completed in 30ms.

22:15:04 [build] 2 page(s) built in 0.83s
22:15:04 [build] Complete!

静的ファイルとして index.html ファイルと/about/index.html ファイルの 2 つが生成されていることがわかります。その後ビルド後のファイル利用して動作確認を行うために”npm run preview”コマンドを実行します。


% npm run preview

> wandering-wind@0.0.1 preview
> astro preview

  🚀  astro  v3.0.8 started in 57ms

  ┃ Local    http://localhost:4321/
  ┃ Network  use --host to expose

Astro のデフォルトでは可能な限り JavaScript を削除した形でビルドファイルは作成されるので JavaScript ファイルは一切利用されていないことが確認できます。

設定前にpreviewで確認
設定前にpreviewで確認

次に View Transitions の設定 によってどのような変化が起こるのかを確認するために View Transitions を設定してビルドを行います。


 % npm run build

> wandering-wind@0.0.1 build
> astro build

22:18:56 [content] No content directory found. Skipping type generation.
22:18:56 [build] output target: static
22:18:56 [build] Collecting build info...
22:18:56 [build] Completed in 81ms.
22:18:56 [build] Building static entrypoints...
22:18:56 [build] Completed in 0.75s.

 building client
Completed in 64ms.


 generating static routes
▶ src/pages/index.astro
  └─ /index.html (+20ms)
▶ src/pages/about.astro
  └─ /about/index.html (+3ms)
Completed in 28ms.

22:18:57 [build] 2 page(s) built in 0.94s
22:18:57 [build] Complete!

building cliet というメッセージが表示されていますがどのようなファイルが作成されるかはビルドのメッセージだけではわかりません。

ビルド後に”npm run preview”コマンドを実行して head タグ の内容を確認します。ビルド後に作成される dist/_astro ディレクトリの hoisted.1a56ad47.js ファイルが読み込まれていることがわかります。script タグの上には View Transitions に関する meta タグも追加されていることがわかります。

設定後にpreviewで確認
設定後にpreviewで確認

さらに”/“から”/about”ページに移動すると html タグに data-astro-trantision 属性も追加されます。

ページ移動時のhtmlタグへの属性の追加
ページ移動時のhtmlタグへの属性の追加

リンクを利用してページを移動すると data-astro-transition の値は”forward”ですが、ブラウザの back ボタンをクリックして移動すると data-astro-transition の値は”back”となります。

ここまでの動作確認で View Transitions に関する以下の 2 点を理解することができました。

  • View Transitions を利用してページをスムーズに移動するためは 2 つのタグを追加することだけで実現可能
  • View Transitions には JavaScript を利用している

View Transitions の無効設定

View Transitions を設定後、ある特定のリンクを利用したページの移動に対してはページ全体の再読み込みを行いたいという場合に a タグに data-astro-reload を追加します。


<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions />
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about" data-astro-reload>About</a></li>
		</ul>
		<slot />
	</body>
</html>

/about から”/“ページへに移動には View Transitions によりアニメーションによるページ移動が行われますが”/“から/about ページへの移動にはページの再読み込みが行われます。data-astro-reload の設定によって View Transitions を無効化することができることがわかりました。

ビルドインアニメーションの設定

View Transitions はアニメーションを使ってページの移動をスムーズに見せています。デフォルトでは fade のアニメーションが設定されておりアニメーションを自由に変更することができます。


ゆっくりと表示したり非表示にするアニメーションを fade と呼びます。

transition:animate の設定

ビルドインアニメーションの値にはデフォルトの fade 以外に initial, slide, none がありますがアニメーションの違いが最も明確な slide にを設定します。アニメーションの設定は transition:animate ディレクティブを利用します。

Layout.astro ファイルの html タグに transition:animate=“slide”を設定します。


<!DOCTYPE html>
<html lang="en" transition:animate="slide">
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions/>
	</head>
	//略

設定後に”/“から”/about”に移動を行うとページのコンテンツ全体が右から左にスライドして表示されるアニメーションに変わることが確認できます。back ボタンを押した場合は左から右にスライドして表示されるアニメーションが設定されます。

transition:animate によってアニメーションを変更できることがわかったので html, head タグをブラウザのデベロッパーツールで確認します。

html, headタグの確認
html, headタグの確認

transition:animate=“slide”を html タグに追加すること新たに CSS ファイルや style タグが追加されていることがわかります。html タグには”data-astro-transition-scope”属性も追加されていることがわかります。

ブラウザでページのソースを確認することで opacity や transform などのスタイルが設定されていることがわかります。

fade のアニメーションを無効にしたい場合は Layout.astro ファイルの html タグに transition:animate=“none”を設定します。none の設定によりアニメーションがなくなりページの再読み込みが行われるようになるわけではなく fade の効果を持つアニメーションはなくなります。値を initial に設定するとブラウザのデフォルトのアニメーションが設定されます。

値が slide の場合は違いがわかりやすいですが他の値の場合は少し違いがわかりにくいかもしれません。

個別要素のアニメーション

ここまでは html タグに transition:animate ディレクティブ を設定しましたが個別の要素に対して設定を行うことができます。index.astro ファイルの h1 タグに設定を行います。


---
import Layout from '../layouts/Layout.astro';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1 transition:animate="slide">Hello Astro 3.0</h1>
	</main>
</Layout>

/about ページから/(ルート)ページに移動した場合のみ”Hello Astro 3.0”の文字列が右から左にスライドして表示されます。

要素を見ると h1 タグに data-astro-transition-scope 属性が設定されていることができます。


<h1 data-astro-transition-scope="astro-y2lp5uny-1">About Astro</h1>

アニメーションのカスタマイズ

ビルトインアニメーションでは fade, slide などの値を設定することができましたがさらにアニメーションをカスタマイズすることもできます。astro:transitions から import した fade 関数の引数に duration を設定することで 2 秒をかけてゆっくりと fade するアニメーションを設定することができます。

---
import Layout from '../layouts/Layout.astro';
import { fade } from 'astro:transitions';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1 transition:animate={fade({ duration: '2s' })}>Hello Astro 3.0</h1>
	</main>
</Layout>

設定した duration については style タグの中で設定されていることが確認できます。

durationの設定
durationの設定

View Transitions ではアニメーションをカスタマイズすることができることがわかりました。

transition:name の設定

View Transitions ではアニメーションが行われる場合に自動的に古いページと新しいページの対応する要素を要素の種類と DOM 内の位置によって識別します。transition:name ディレクティブ を利用することで開発者が対応する要素を指定して対応する要素に対してアニメーションを設定することができます。言葉の説明よりも実際に動作確認した方がわかりやすいのでAstro のサイトから取得した画像を利用して動作確認を行います。利用する画像の名前は”astro-logo-dark.png”です。ダウンロード後に public ディレクトリに保存します。


---
import Layout from '../layouts/Layout.astro';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1>Hello Astro 3.0</h1>
		<img src="/astro-logo-dark.png" width="100px"/>
	</main>
</Layout>

ロゴ画像が表示されることを確認します。

ロゴ画像の表示
ロゴ画像の表示

about.astro ファイルでも画像を設定しますがアニメーションの効果がはっきりとわかるようように style 属性を利用して画面の右上に表示するように設定します。


---
import Layout from '../layouts/Layout.astro';
---

<Layout title="About Page">
    <div style="position:absolute; top:0; right:0">
        <img src="/astro-logo-dark.png" width="500px" />
    </div>
	<main>
		<h1>About Astro</h1>
	</main>
</Layout>
aboutページでのロゴの設定
aboutページでのロゴの設定

index.astro, about.astro での画像の設定後、ページの移動を行います。ページ全体が fade のアニメーションによってスムーズに移動します。

transition:name ディレクティブ の設定前の動作が確認できたので index.astro, about.astro の img タグに transition:name=“logo”の同じ値を設定します。値の logo は任意の名前で対応する要素を識別するために設定します。

---
import Layout from '../layouts/Layout.astro';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1>Hello Astro 3.0</h1>
		<img src="/astro-logo-dark.png" width="100px" transition:name="logo"/>
	</main>
</Layout>

---
import Layout from '../layouts/Layout.astro';
---

<Layout title="About Page">
    <div style="position:absolute; top:0; right:0">
        <img src="/astro-logo-dark.png" width="500px" transition:name="logo"/>
    </div>
	<main>
		<h1>About Astro</h1>
	</main>
</Layout>

transition:name を設定することで”/“から/about に移動すると”/“では小さなロゴ画像がアニメーションによりゆっくりと大きくなり移動しながら右上に表示されるようになります。/about から”/“に移動すると今度は大きなロゴがゆっくりと小さくなりながら移動するアニメーションが設定されます。さまざまな動きのあるアニメーションを設定できることがわかりました。

transition:persist の設定

ページを移動してもそのままの状態を保持したいコンポーネントや要素を transition:persist ディレクティブ で設定することができます。最初に video タグに transition:persist ディレクティブ を設定することで再生中の動画をページ移動後にリセットされることなく継続して再生し続けることを確認します。次に React で useState Hook を利用して状態を定義してカウンターを作成します。ページの移動前に増減したカウンターの値がページ移動後もその値を保持できるか確認します。

video タグによる動作確認

Layout.astro ファイルに video タグを追加します。


import { ViewTransitions } from 'astro:transitions';
interface Props {
	title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en" >
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions />
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about">About</a></li>
		</ul>
		<video controls="" autoplay="" transition:persist>
			<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
		</video>
		<slot />
	</body>
</html>

/(ルート)にアクセスすると動画が再生されます。

アクセスして動画を再生
アクセスして動画を再生

/about ページに移動しても再生中の動画はそのままの状態で表示されます。

ページを移動しても動画が継続して再生
ページを移動しても動画が継続して再生

transition:persist ディレクティブを利用しない場合はどのような動作になるのか確認するために video タグから transition:persist ディレクティブ を削除します。

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

transition:persist ディレクティブ を削除するとページを移動する度に動画はリセットされ最初から再生されます。

Layout.astro ファイルはレイアウトファイルなので index.astro, about.astro で共有しているファイルで index.astro, about.astro で同じ video タグを設定した場合の動作確認を行います。


---
import Layout from '../layouts/Layout.astro';
import { fade } from 'astro:transitions';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1>Hello Astro 3.0</h1>
		<video controls="" autoplay="" transition:persist>
			<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
		</video>
	</main>
</Layout>

---
import Layout from '../layouts/Layout.astro';
---

<Layout title="About Page">
    <main>
		<h1>About Astro</h1>
        <video controls="" autoplay="" transition:persist>
            <source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
        </video>
	</main>
</Layout>

どちらのファイルの video タグに transition:persist ディレクティブ をつけていますがページを移動すると再生はリセットされます。2 つの video タグの DOM 要素のペアを関連付けるため transition:name ディレクティブ を利用します。transition:name ディレクティブには両方のタグで任意の同じ名前を設定します。ここでは media-player としています。


---
import Layout from '../layouts/Layout.astro';
import { fade } from 'astro:transitions';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1>Hello Astro 3.0</h1>
		<video controls="" autoplay="" transition:persist transition:name="media-player">
			<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
		</video>
	</main>
</Layout>

---
import Layout from '../layouts/Layout.astro';
---

<Layout title="About Page">
    <main>
		<h1>About Astro</h1>
        <video controls="" autoplay="" transition:persist transition:name="media-player">
            <source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
        </video>
	</main>
</Layout>

設定後ページを移動しても動画がリセットされることなくそのま継続して再生されます。

transition:name ディレクティブ を利用せず、transition:persist ディレクティブ に値を設定することができます。


---
import Layout from '../layouts/Layout.astro';
import { fade } from 'astro:transitions';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1>Hello Astro 3.0</h1>
		<video controls="" autoplay="" transition:persist="media-player">
			<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
		</video>
	</main>
</Layout>

カウンターコンポーネント による動作確認

React の useState Hook で定義した状態をページが移動しても保持できるか確認していきます。

React を Astro で利用するためには React Integrations のインストールが必要となります。“npx astro add react”コマンドを実行します。


 % npx astro add react
✔ Resolving packages...

  Astro will run the following command:
  If you skip this step, you can always run it yourself later

 ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
 │ npm install @astrojs/react @types/react-dom@^18.0.6 @types/react@^18.0.21 react-dom@^18.0.0 react@^18.0.0  │
 ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

✔ Continue? … yes
✔ Installing dependencies...

  Astro will make the following changes to your config file:

 ╭ astro.config.mjs ─────────────────────────────╮
 │ import { defineConfig } from 'astro/config';  │
 │                                               │
 │ import react from "@astrojs/react";           │
 │                                               │
 │ // https://astro.build/config                 │
 │ export default defineConfig({                 │
 │   integrations: [react()]                     │
 │ });                                           │
 ╰───────────────────────────────────────────────╯

✔ Continue? … yes

   success  Added the following integration to your project:
  - @astrojs/react

  Astro will make the following changes to your tsconfig.json:

 ╭ tsconfig.json ──────────────────────────╮
 │ {                                       │
 │   "extends": "astro/tsconfigs/strict",  │
 │   "compilerOptions": {                  │
 │     "jsx": "react-jsx",                 │
 │     "jsxImportSource": "react"          │
 │   }                                     │
 │ }                                       │
 ╰─────────────────────────────────────────╯

✔ Continue? … yes

   success  Successfully updated TypeScript settings

Integration の追加が完了したら components ディレクトリに Counter.tsx ファイルを作成して以下のコードを記述します。


import { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <>
      <h1>React Counter</h1>
      <div>{count}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>increment</button>
        <button onClick={() => setCount(count - 1)}>decrement</button>
      </div>
    </>
  );
};

export default Counter;

Layout.astro ファイルで Counter コンポーネントを import します。


---
import { ViewTransitions } from 'astro:transitions';
import Counter from "../components/Counter"
interface Props {
	title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en" >
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions />
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about">About</a></li>
		</ul>
		<Counter client:load />
		<slot />
	</body>
</html>
Counter タグにはブラウザ側の JavaScript を利用するため client:load が必須です。client:load がない場合はボタンをクリックしてもカウンターの値は 0 のままです。

ボタンをクリックするとカウンターの数が増減します。

カウンターの表示
カウンターの表示

”/“から/about のページに移動するとカウンターの値はリセットされます。

ページ移動後のカウンターのリセット
ページ移動後のカウンターのリセット

カウンターの値をページを移動しても保持するために transition:persist ディレクティブ を設定します。video タグのような要素だけではなくコンポーネントでも設定することができます。


<Counter client:load transition:persist />

ボタンをクリックしてカウンターを増やします。

カウンターの表示
カウンターの表示

”/“から/about に移動します。設定後はカウンターを増減させた後にページを移動しても Counter の値を保持することができます。/about から”/“に戻ってもカウンターの値は保持したままです。

カウンターの値の保持
カウンターの値の保持

transition:persist ディレクティブによってページを移動しても状態を保持できることが確認できました。

Fallback の設定

View Transitions はブラウザが持つ View Transitions Browser API を利用していますがすべてのブラウザが View Transitions Browser API をサポートしているわけではありません。サポートをしていないブラウザも利用できるように JavaScript を利用しています。

View Transitions Browser API をサポートしていないブラウザでは fallback を設定することで View Transitions を制御することができます。デフォルトでは fallback の値は animate に設定されています。設定値は head タグの meta タグの astro-view-transitions-fallback の content の値で確認することができます。


<meta name="astro-view-transitions-fallback" content="animate">

View Transitions Browser API がサポートされていない Firefox ブラウザを利用して動作確認を行います。fallback の値を”none”に設定するとサポートしていないブラウザではページを移動する度にページ全体の再読み込みを行います。設定は ViewTransitions タグで行います。


---
import { ViewTransitions } from 'astro:transitions';
import Counter from "../components/Counter"
interface Props {
	title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en" >
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions fallback="none"/>
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about">About</a></li>
		</ul>
		<slot />
	</body>
</html>

アニメーションが設定されているかはっきりとわかるように fade 関数を利用しています。


---
import Layout from '../layouts/Layout.astro';
import { fade } from 'astro:transitions';
---

<Layout title="Welcome to Astro.">
	<main>
		<h1 transition:animate={fade({ duration: '2s' })}>Hello Astro 3.0</h1>
	</main>
</Layout>

meta タグの値を確認すると値が none になっていることが確認できます。


<meta name="astro-view-transitions-fallback" content="none">

fallback の値を”none”に設定後にページを移動すると移動する度にページの再読み込みが行われます。同じ設定で Chrome ブラウザでページを移動すると fade 関数のアニメーションが実行されます。

Firefox ブラウザでも fallback=“animate”にするか fallback を設定しない場合はアニメーションが実行されます。

fallback の値を swap にした場合は fade 関数で設定したアニメーションは実行されませんがページ全体の再読み込みは行われずページの内容は移動するとすぐに書き換えられて移動したページの内容が表示されます。

このように fallback の値を利用して View Transitions の動作を変更することができます。

イベントの設定

ページを移動する際に発火されるイベントが 2 つ準備されています。ページの移動が完了した場合に発火される page-load と古いページが新ページに入れ替わった直後に発火される after-swap です。2 つのイベントを Layout.astro ファイルの script タグで設定します。


---
import { ViewTransitions } from 'astro:transitions';
import Counter from "../components/Counter"
interface Props {
	title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en" >
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Astro description">
		<meta name="viewport" content="width=device-width" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="generator" content={Astro.generator} />
		<title>{title}</title>
		<ViewTransitions/>
	</head>
	<body>
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/about">About</a></li>
		</ul>
		<slot />
	</body>
</html>

<script>
	document.addEventListener('astro:page-load', () => {
	  console.log('page-load')
	});
	document.addEventListener('astro:after-swap', () => {
	  console.log('after-swap')
	});
</script>

リンクをクリックした直後にコンソールには”after-swap”が表示されます。ページのロードが完了したら”page-load”が表示されます。fade({ duration: ‘2s’ })を設定している場合には 2 秒経過してページの内容が表示された後に”page-load”が表示されます。

astro:page-load はアクセス直後だけに一度だけ実行することができます。アクセス直後に一度だけ表示され、その後ページを移動してもイベントが発火されることはありません。設定は下記のように行います。


<script>
	document.addEventListener('astro:page-load', () => {
	  console.log('page-load')
	}, { once: true });
</script>

ここまでの動作確認で View Transitions の基本的な設定を確認することができました。