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

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を利用して対応させています。
fukidashi
マイナーのバージョンアップをする度にView Transitionsに関する機能の強化が行われているので一部内容が古くなっている可能性があります。
fukidashi

環境の構築

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


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

> npx
> create-astro
 astro   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

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict

  deps   Install dependencies?
         Yes

   git   Initialize a new git repository?
         Yes

      ✔  Project initialized!
         ■ Template copied
         ■ TypeScript customized
         ■ Dependencies installed
         ■ 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”のページ間で移動できるように設定を行います。またstyleタグのhtmlのbackgroundの設定をコメントしておきます。


---
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>
<style is:global>
//略
	html {
		font-family: system-ui, sans-serif;
		/* background: #13151a; */
	}
//略

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

10:41:05 [types] Generated 2ms

 astro  v4.14.3 ready in 296 ms

  ┃ 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タグを2つ確認することができますが 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 check && astro build

10:57:31 [types] Generated 85ms
10:57:32 [check] Getting diagnostics for Astro files in /Users/mac/Desktop/wandering-wind...
Result (7 files): 
- 0 errors
- 0 warnings
- 0 hints

10:57:35 [types] Generated 59ms
10:57:35 [build] output: "static"
10:57:35 [build] directory: /Users/mac/Desktop/wandering-wind/dist/
10:57:35 [build] Collecting build info...
10:57:35 [build] ✓ Completed in 68ms.
10:57:35 [build] Building static entrypoints...
10:57:36 [vite] ✓ built in 587ms
10:57:36 [build] ✓ Completed in 621ms.

 generating static routes 
10:57:36 ▶ src/pages/about.astro
10:57:36   └─ /about/index.html (+7ms)
10:57:36 ▶ src/pages/index.astro
10:57:36   └─ /index.html (+2ms)
10:57:36 ✓ Completed in 21ms.

10:57:36 [build] 2 page(s) built in 726ms
10:57:36 [build] Complete!

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


% npm run preview

> wandering-wind@0.0.1 preview
> astro preview

astro  v4.14.3 ready in 14 ms

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

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

設定前の状態をpreviewでheadタグを確認
設定前の状態をpreviewでheadタグを確認

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


% npm run build  

> wandering-wind@0.0.1 build
> astro check && astro build

11:00:27 [types] Generated 83ms
11:00:27 [check] Getting diagnostics for Astro files in /Users/mac/Desktop/wandering-wind...
Result (6 files): 
- 0 errors
- 0 warnings
- 0 hints

11:00:31 [types] Generated 38ms
11:00:31 [build] output: "static"
11:00:31 [build] directory: /Users/mac/Desktop/wandering-wind/dist/
11:00:31 [build] Collecting build info...
11:00:31 [build] ✓ Completed in 46ms.
11:00:31 [build] Building static entrypoints...
11:00:31 [vite] ✓ built in 590ms
11:00:31 [build] ✓ Completed in 624ms.

 building client (vite) 
11:00:31 [vite] ✓ 12 modules transformed.
11:00:31 [vite] dist/_astro/hoisted.D-2wbxhY.js  14.17 kB │ gzip: 4.84 kB
11:00:31 [vite] ✓ built in 80ms

 generating static routes 
11:00:31 ▶ src/pages/about.astro
11:00:31   └─ /about/index.html (+8ms)
11:00:31 ▶ src/pages/index.astro
11:00:31   └─ /index.html (+2ms)
11:00:31 ✓ Completed in 19ms.

11:00:31 [build] 2 page(s) built in 790ms
11:00:31 [build] Complete!

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

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

設定後の状態をpreviewでheadタグを確認
設定後の状態をpreviewでheadタグを確認

ここまでの動作確認で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 ページへの移動にはページの再読み込みが行われます。aタグにdata-astro-reloadを設定することでView Transitionsを無効化することができることがわかりました。

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

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

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

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ファイルが追加されていることがわかります。htmlタグには”data-astro-transition-scope”属性も追加されていることがわかります。

ブラウザでページのソースを確認することでopacityやtransformなどのスタイルが設定されていることがわかります。node_modules/astro/components/viewtransition.cssでも確認できます。」
fukidashi

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

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

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

ここまでは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-source-file="/Users/mac/Desktop/wandering-wind/src/pages/index.astro" data-astro-source-loc="7:34" data-astro-transition-scope="astro-benpfmuv-1">Hello Astro 3.0</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:nameを利用することでさまざまな動きのあるアニメーションを設定できることがわかりました。

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タグを設定した場合の動作確認を行います。Layout.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>

動作確認が完了したら、次の動作確認のためindex.astro, about.astroファイルからvideoタグを削除しておきます。

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

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@^3.6.2 @types/react@^18.3.4 @types/react-dom@^18.3.0 react@^18.3.1 react-dom@^18.3.1  │
 ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

✔ 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のままです。
fukidashi

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

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

”/“から/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を設定しない場合はアニメーションが実行されます。

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

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

イベントの設定

ページを移動する際に発火されるイベントが5つ準備されています。View Transitionが登場した当初から存在するイベントは2つでページの移動が完了した場合に発火される page-load と古いページが新ページに入れ替わった直後に発火されるafter-swapです。残りの3つはbefore-preparation, after-preparation, before-swapでバージョン3.6で追加されました。

2つのイベントpage-loadとafter-swapを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 の基本的な設定を確認することができました。