Astro v3 の View Transitions を追加してみました
Astro の v3 にて、 View Transitions というものが追加されました。
このブログも Next.js から Astro に移行したので、ついでに触ったり調べたりしました。
View Transitions とは
一言でいうと、ページ間トランジションをView Transitions APIベースで簡単に実装できる機能です。
View Transitions API については、ICSさんの記事 View Transitions API入門 - 連続性のある画面遷移アニメーションを実現するウェブの新技術 がとても参考になると思います。
このブラウザAPIとCSSをベースに、Astroでは独自の機能を提供しています。デフォルトのアニメーションや、未対応ブラウザのフォールバックなどは地味に嬉しいですね。
- fadeやslide、noneなど、いくつかの組み込みのアニメーションオプション。
- 戻る・進むの両ナビゲーションでのアニメーションのサポート。
- トランジションアニメーションのすべての要素を完全にカスタマイズし、さらに独自のアニメーションを作成する機能。
- 非ページへのリンクに対しクライアントサイドナビゲーションを防止するオプション。
- まだView Transition APIをサポートしていないブラウザのためのフォールバック用動作の制御。
- prefers-reduced-motionの自動サポート。
ちなみに、ベースとなる CSS View Transitions Module Level 1 は執筆時点では CR(勧告候補) になっています。
実際に追加してみる
それでは、実際に追加します。
<ViewTransitions>
タグを追加する
ページ全体のトランジションを実装するのであれば、 Astro から提供されている <ViewTransitions />
コンポーネントを、各ページの <head>
タグ内に追加するだけで機能します。
当ブログでは layouts/BaseLayout.astro
というベースのレイアウトファイルを作成していたため、そこに追加するだけですべてのページで利用できるようになりました。
---
// BaseLayout.astro
import BaseHead from "~/components/BaseHead.astro"
import { ViewTransitions } from 'astro:transitions';
---
<html lang="ja">
<head>
<BaseHead />
<ViewTransitions /> <!-- ← 追加 -->
</head>
<body><slot /></body>
</html>
たったこれだけで、ページ遷移ごとにフェードのトランジションが追加されます。
デフォルトのアニメーションはフェードイン/アウトが指定されています。
内部的には、以下をおこなっているそうです。
- クライアントサイド用の簡単なルーター機能(トランジション自体がクライアントサイドじゃないと機能しないため)
- ナビゲーションを中断してページ間トランジションを実装者がカスタマイズできるようにする
これで、ページ全体のトランジションは実装完了しました。おつかれさまでした!
個別に要素を指定する
これだけでは味気がないので、ページ前後のコンポーネントを紐づけて、置き換わっているような表現に近づけたいと思います。
今回の対象は以下の2箇所です。
- トップページのアイコン+各種リンク:記事ページのヘッダー
- トップページの一覧:記事ページのコンテンツ全体
トップページのアイコン+各種リンク:記事ページのヘッダー
対象のコンポーネントとしては、 <MainProfile>
と <Header>
というコンポーネントです。
理由はよくわかってないですが、コンポーネントに対して transition:xxx
属性をつけても機能しませんでした。
(VS Code 拡張が型的につけれるように解釈してる?)
そのため、呼び出し側で DOM でラップして transition:name
をつけるように対応しました。
-
pages/index.astro
<BaseLayout> <main class="p-6 grid items-center justify-center gap-9 content-center min-h-screen"> + <div transition:name="profile"> <MainProfile /> + </div> <ScrapsSection scraps={scraps} /> </main> </BaseLayout>
-
pages/scrap/[...slug].astro
- <div class="sticky top-0 left-0 z-50 isolate"> + <div class="sticky top-0 left-0 z-50 isolate" transition:name="profile"> <Header /> </div>
トップページの各記事へのリンク:記事ページのコンテンツ全体
一覧となっているリンクとそれに対応する記事ページのコンテンツに対しては、 transition:name
の値にスラッグ名を追加しました。
transition:name
の元である view-transition-name
がページ内で一意でないと動作しないため、
- ページ内でユニークになる
- 各ページ間で共通の値になる
というデータを利用し、設定しました。
DOM の id
属性とおなじような扱いと思うといいかもしれません。
-
ScrapsSection.astro
{scraps.map(({ slug, data: { title, pubDate } }) => ( <li class="pt-3 pb-3 border-b border-neutral-200 last-of-type:border-b-0" + transition:name={`content-${slug}`} > <time class="block mb-1 font-light text-sm"> {dayjs(pubDate).format('YYYY-MM-DD')} </time> <a class="block hover:underline" href={`/scrap/${slug}`}> {title} </a> </li> ))}
-
pages/scrap/[...slug].astro
- <main class="px-3 py-12 max-w-screen-sm mx-auto isolate"> + <main class="px-3 py-12 max-w-screen-sm mx-auto isolate" transition:name={`content-${slug}`}> <time class="block mb-2">{pubDate}</time> <article class="
動作を確認する
ここまでの実装で、今のサイトの表示のようなページ間トランジションが実現できます。
気になった点
とても簡単にページ間トランジションが実装できる一方で、気になる点は何個かあがりました。
- 遷移先のファイル読み込みまちによって、クリックからページ間トランジションが発生するまでのフィードバックに遅延が発生する
- トランジション間にローディングなどの処理をかませる方法が存在しなさそう
対応ブラウザについて
ここまで実装した View Transitions ですが、 API 自体は Google Chrome が先行して対応しているため、 Google Chrome では動作します。
“View Transition” | Can I use… Support tables for HTML5, CSS3, etc を見る限りは Edge も動作するようです。Safari / Firefox については、執筆時点では対応されていませんでした。
ただし、 Safari / Firefox に関しては、 Astro の提供する View Transitions のうち、ルート要素に適用したものについては現状でも動作していました。
さいごに
各ブラウザの動作保証ができないため、まだ本番環境で運用するには早いもののように感じました。
しかし、ページ間トランジションをたったこれだけで簡単に実装できるという点は技術の進歩を感じます。
使いたくなったときに思い出してみるといいかもしれません。