[JSなし] パワーアップしたView Transitions APIでMPAをSPAみたいにする

2024年6月11日リリースのChrome126から、View Transitions APIがパワーアップし、別ページへの遷移でも使うことができるようになったので紹介したいと思います。

# View Transitions APIとは

View Transitions APIは、画面遷移をなめらかに行うことができる機能です。

https://developer.chrome.com/docs/web-platform/view-transitions?hl=ja

https://developer.mozilla.org/ja/docs/Web/API/View_Transitions_API

この機能を使うと、以下の動画で紹介されているように、ページ遷移にアニメーションをつけることができます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/eY6C_-aDdTo?si=bqXxYBw-JOEC3pvr&amp;start=408" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

まずはView Transitions APIの特徴から紹介したいと思います。

### 特徴1：異なるDOM要素間でアニメーションできる

アニメーション前後のDOM構造が異なっていても、アニメーションを行うことが可能です。

例えば、`<div>`がアニメーションで変化しつつ`<img>`要素になる、といったアニメーションが可能です。

### 特徴2：アニメーションがCSSで自由自在

アニメーションはCSSでカスタマイズすることも可能です。
デフォルトではクロスフェードのアニメーションが適用されます。

例えばページ遷移時に新しいページが左や右から出てくるというエフェクトや、古いページを縮小しつつフェードアウトさせるといった表現もできます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/eY6C_-aDdTo?si=WbZE6y5A-S3nQzuG&amp;start=708" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

[View Transitions API入門 - 連続性のある画面遷移アニメーションを実現するウェブの新技術](https://ics.media/entry/230510/)という記事では具体的な実装例が掲載されており、デモサイトもあるので、ぜひ触って試してみてください。

https://ics.media/entry/230510/

### 特徴3：別ページへの遷移でもアニメーションできる

従来のCSSアニメーションでは同一ページ内の要素をアニメーションすることしかできませんでした。
また、View Transitions APIについても、これまで実装されていたのは同一ページ内のアニメーションのみでした。

しかし、**2024年6月11日リリースのChrome126**から、**別ページへの遷移でもView Transitions APIを使うことができるようになりました！**

この機能を使うと、従来SPA(Single-Page-Application)でしか表現できなかったリッチなアニメーションが、MPA(Multi-Page-Application)で構築されているWebサイトでも利用することができます！

（※ただし同一オリジンのサイト間の遷移のみ）

本記事では、Chrome126から導入される、別ページへの遷移の際にアニメーションを行う機能について解説したいと思います。

:::note warn
本記事公開時点では、まだChrome126はリリースされておりませんのでご注意ください。
:::

# 別ページへの遷移でView Transitions APIを使う方法

### 1. View Transitions APIの有効化

以下のCSSを記述するだけです。

```css
@view-transition {
  navigation: auto;
}
```

View Transitions APIを有効化したいページにこのCSSを書くだけで、ページ遷移時にフェードイン・フェードアウトするアニメーションが自動的に適用されます。

以下の例では、ページ遷移の際に、古いページのコンテンツがフェードアウトしつつ、新しいページがフェードインしてくるようになっているのがわかるかと思います。

![画面収録 2024-06-09 17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/888a1c45-a5b6-665d-90d9-715f97d99287.gif)

### 2. 要素同士のアニメーション

上記の例ではページ全体に対してフェードイン・フェードアウトのアニメーションが適用されました。
それに加えて、特定の要素にアニメーションを付けるにはCSSの`view-transition-name`を使います。

```css:遷移前のページに適用するCSS
@view-transition {
  navigation: auto;
}
#article-link { /* アニメーションを付けたい要素 */
  /* ページ遷移の前後で同じview-transition-nameの要素同士がアニメーションされる */
  view-transition-name: article;
}
```

```css:遷移後のページに適用するCSS
@view-transition {
  navigation: auto;
}
#article-detail { /* アニメーションを付けたい要素 */
  /* ページ遷移の前後で同じview-transition-nameの要素同士がアニメーションされる */
  view-transition-name: article;
}
```

下の動画のように、共通の`view-transition-name`を持つ要素同士がアニメーションされます。

![画面収録 2024-06-09 14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/d83dfb3a-20a3-0eff-58a3-e8be37cc62d7.gif)

:::note warn
1つのページ内で`view-transition-name`を重複させることはできません。
逆に言うと、サーバーサイドの処理やJavaScriptの処理をうまく使って`view-transition-name`を一意になるように割り振る必要があります。
例えば記事リストから記事詳細ページへのリンクの場合、記事リストページでは記事ごとに一意な`view-transition-name`を指定する必要があります。

#### JSで処理する場合

JavaScriptで実現するためには`pageswap`イベントと`pagereveal`イベントを使います。
これらのイベントは、ページ遷移の前後に発生します。そのタイミングで動的に`view-transition-name`を付与する方法が、[Chromeの開発者サイト](https://developer.chrome.com/docs/web-platform/view-transitions/cross-document?hl=ja)で解説されています。

https://developer.chrome.com/docs/web-platform/view-transitions/cross-document?hl=ja

#### サーバーサイドで処理する場合

サーバーサイドで一意な`view-transition-name`を割り振る場合は、以下のように`view-transition-name`に一意なIDを含めてしまえば良さそうです。

```php:サーバーサイドの実装イメージ(PHP)
<?php foreach ($articles as $article) { ?>
    <a
      class="article-link"
      style="view-transition-name: article-<?php echo $article.id ?>"
    >
      <img src="...">
      記事の詳細はこちら
    </a>
<?php } ?>
```

また、CSS変数などを活用することもできそうです。
:::

### 3. さまざまなエフェクトをかける

デフォルトではフェードイン・フェードアウトのアニメーションになりますが、これを変更することも可能です。

アニメーションを適用するには、`::view-transition-{old,new}`を使って`animation`プロパティにCSSアニメーションを指定します。

こちらですが[MDNの例](https://developer.mozilla.org/ja/docs/Web/CSS/::view-transition-old)が分かりやすかったので引用したいと思います。
（コメント部分は筆者の追記です。）

```css:MDNより引用
/* https://developer.mozilla.org/ja/docs/Web/CSS/::view-transition-old より引用 */

/* アニメーションを定義 */
@keyframes grow-x {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

 /* アニメーションを定義 */
@keyframes shrink-x {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

/* 遷移元ページのアニメーション */
::view-transition-old(figure-caption) {
  animation: 0.25s linear both shrink-x; /* ここに適用したいアニメーションの名前を指定 */
}

/* 遷移後ページのアニメーション */
::view-transition-new(figure-caption) {
  animation: 0.25s 0.25s linear both grow-x; /* ここに適用したいアニメーションの名前を指定 */
}
```

また、上で紹介した以外にも、[Chromeの開発者サイト](https://developer.chrome.com/docs/web-platform/view-transitions/cross-document?hl=ja)ではさまざまなアニメーションが例示されているので、本記事でもその一部を引用して紹介したいと思います。（コメント部分は筆者の追記です。）

https://developer.chrome.com/docs/web-platform/view-transitions/cross-document?hl=ja

```css
/* https://developer.chrome.com/docs/web-platform/view-transitions/cross-document?hl=ja より引用 */

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}

@keyframes scale-up {
  from {
    scale: 0;
  }
}

@keyframes scale-down {
  to {
    scale: 0;
  }
}


/* 縮小しながらフェードアウト */
@keyframes shrink {
  to {
    scale: 0.8;
    opacity: 0.5;
  }
}

/* 拡大しながらフェードイン */
@keyframes grow {
  from {
    scale: 0.8;
    opacity: 0.5;
  }
}
```


### アニメーションがしっくりこない時のTips

上で紹介したようにCSSアニメーションを適用するだけで充分リッチなUIになりますが、アニメーションしている要素によっては微妙なサイズのずれが気になったりすることがあります。

そういう時は[View transitions: Handling aspect ratio changesというブログ記事](https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/)で解説されているテクニックを使うと良さそうです。
以下、当該記事内で紹介されているコードをいくつか引用したいと思います。

https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/

例えば、テキスト同士のアニメーションで、微妙な文字のずれが生じている場合、`width: fit-content`を使って解決する方法が紹介されています。

```css
/* https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/ より引用 */
#view-transition-target {
  view-transition-name: simple-text;
  width: fit-content;
}
```

また、アニメーション前後で要素のアスペクト比が変わるとアニメーションがうまくいかない問題については、`::view-transition-{old,new}`に`height: 100%`などをかけておくといいようです。

```css
/* https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/ より引用 */
::view-transition-old(text),
::view-transition-new(text) {
  /* Break aspect ratio at an element level */
  height: 100%;
  /* But maintain it within the image itself */
  object-fit: none;
  /* And hide parts of the image that go out of bounds */
  overflow: clip;
}
```

他のTipsについても[紹介したブログ記事](https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/)内に掲載されているので、ぜひ参考にしてみてください！

# まとめ

本記事ではChrome 126から導入される、MPA向けView Transitions APIについて紹介しました。

この機能を使うと、従来難しかったDOM要素が変更される時のアニメーションや、ページ遷移時のアニメーションを実現することができます。

また、CSSで実装する機能なので、非対応ブラウザでは通常のページ遷移を行いつつ、対応ブラウザへはリッチなUIを提供することができるというのも嬉しいポイントですね！

まだ新しい機能ということで日本語情報も少ないですが、まるでスマホアプリを使っているようなUIをWebでも提供できるということで、これから積極的に使っていきたいです！