.NET9 から追加されたイベントを使い、Blazor SSR でページ遷移時の View Transition 効果を実現する

## Blazor は SPA だけじゃなくて SSR オンリーな Web アプリも書ける

2023年11月リリースの .NET 8 から、Blazor のコンポーネントモデルで、いわゆる MPA (Multi Page Application) とでも言えばいいでしょうか、静的サーバー側レンダリングアプリケーションを実装できるようになりました。つまり、ブラウザと Web サーバー間での HTTP GET や HTTP POST 要求の往復のみで機能する、そういった形態の Web アプリケーションが、Blazor の構文とコンポーネントで実装できるということです。

ただ、本当にページ遷移をブラウザの機能に任せるのではなく、少量の JavaScript コードをページに読み込ませることで、**「拡張ナビゲーション」** と呼ばれる、Ruby on Rails ですと古くは Turbo Link と呼ばれていた機能に相当するでしょうか、ページ遷移時のちらつきなどを軽減する機能が、標準で組み込まれるようになっています。

そのため、Blazor SSR アプリケーションにおいては、ブラウザ上の JavaScript にて、`<a>` タグをクリックなどによるページ遷移時の、**ページ読み込み完了時のイベント**を捕捉し、独自の JavaScript コード処理を加えることもできるようになっていました。

具体的には、以下のように JavaScript 側コンテキストにおける `Blazor` オブジェクトに対し、`addEventListener` メソッドを、イベント名である `"enhancedload"` と、そのハンドラ関数を引数に呼び出すことで実装します。

```js
Blazor.addEventListener('enhancedload', () => {
    // ここで何かする
});
```

## .NET 9 からページ遷移開始前後のイベントも使えるように

この JavaScript 側のページ遷移時イベントは、.NET 8 までは、ページ遷移が完了したときのイベントのみだったのですが、 .NET 9 ではさらに、これからページ遷移が始まる直前のイベントも追加されました。具体的には、'enhancedload' とは別に、`enhancednavigationstart` と `enhancednavigationend` という、`～start` `～end` の対のイベントが追加となっています。

```js
Blazor.addEventListener('enhancednavigationstart', () => {
    // ページ遷移が始まる前に、ここが呼び出される
});

Blazor.addEventListener('enhancednavigationend', () => {
    // ページ遷移が完了した後で、ここが呼び出される
});
```

## Blazor SSR で View Transition を使ってみよう

そこでこれらページ遷移前と遷移後のイベントを捕捉することで、ブラウザの View Transion API を利用し、ページ遷移間でのコンテンツの滑らかな遷移アニメーション効果を、Blazor SSR アプリケーション上で実現できるようになります。

例えば以下のような、一覧-詳細ページを往復するような Blazor SSR アプリケーションがあるとします。

![movie-001.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/728f8319-f210-31d9-251b-dfa2ac5f3c69.gif)

この Blazor SSR アプリケーションに、以下の JavaScript ファイルを `<script />` タグで読み込むように組み込むと...

```js:wwwroot/js/transition.js
let _tcs = null;

// ページ遷移が始まるときのイベントをハンドル
Blazor.addEventListener("enhancednavigationstart", () => {
    // (View Transition API が実装されていないブラウザの場合は何もしない)
    if (!document.startViewTransition) return;

    // ブラウザの startViewTransition を呼び出して View Transition を開始
    _tsc = Promise.withResolvers();
    document.startViewTransition(() => _tsc?.promise);
});

// ページ遷移が完了したときのイベントをハンドル
Blazor.addEventListener("enhancednavigationend", () => {

    // ブラウザの startViewTransition を呼び出したときに渡した Promise を
    // 解決して、ブラウザにページ遷移が完了したことを知らせることで、
    // 2つのページ表示間がアニメーション効果でつないで表示される。
    _tsc?.resolve();
});
```

```html:Components/App.razor
<!DOCTYPE html>
<html lang="en">
    ...
    <script src="_framework/blazor.web.js"></script>

    <!-- 👇 これを追加 -->
    <script src="@Assets["js/transition.js"]" type="module"></script>

</body>
</html>
```

および、いくつか CSS プロパティ定義も書き足すことで、下図のように、ページ遷移間が滑らかな遷移アニメーション効果でつながって表示されるようになります。

![movie-002.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/60116289-e336-c239-bfd9-23bec82854fe.gif)

このデモンストレーションサイトのソースコード一式は下記 GitHub リポジトリにて公開していますので、適宜参照ください。

https://github.com/sample-by-jsakamoto/Blazor-ViewTransition-on-SSR-NET9

また上記デモンストレーションサイトは、以下の URL で公開していますので、上記アニメーション GIF ではわかりにくいヌルヌル感は、以下の URL を実際に開いて体感してください。

https://demo-blazor-viewtransition-ssr-net9.azurewebsites.net/

## 注意点

View Transition API は 2024年11月現在では Firefox ではサポートされておらず、古い iOS の Safai ブラウザでもサポートされていません。それら View Transion API を未サポートのブラウザでエラーで停止してしまわないよう、View Transition API が使えるのかどうかのチェックを行なうよう、ご注意ください。

とはいえ、View Transion の利用に限らず、Blazor SSR におけるページ遷移前後のイベントは、何かしら利用の場面があるかもしれません。とくに、古典的な jQuery ウィジェットを組み込んでいる場合にそのウィジェットの初期化と破棄を行なうとか、アプリケーションパフォーマンスモニタリングサービスの利用時に、ページ遷移イベントを通知する、などの用途も想像されます。知っておくとよいでしょう。