シングルページ アプリケーションのビュー遷移

ウェブページでよく使われるパターンは、JavaScript を使用して、新しい完全な HTML ドキュメントを読み込まずに、ページ上のコンテンツを動的に置き換えることです。これはシングルページ アプリケーション(SPA)と呼ばれます。ビュー遷移を使用すると、SPA のページ間の継続性やコンテキストを示すことができます。

ページ全体のトランジション

ユーザーが SPA の新しいビューに移動すると、フレームワークは DOM を新しいコンテンツに置き換えます。この方法ではコンテンツがただ表示されるだけですが、現在のコンテンツと新しいコンテンツの間にトランジションを設けたい場合はどうすればよいでしょうか?

多くの場合、トランジションでは古いビューと新しいビューが同時に表示されます。たとえば、古いビューがフェードアウトするのと同時に、新しいビューがフェードインします。既存のコンテンツが置き換えられるため、ビューの切り替えの前はこれが課題でした。

ビュー遷移を使用するには、DOM を変更するロジックをコールバックでラップする必要があります。これらの例では、MyRouter というウェブ コンポーネントによって提供される基本的なルーター実装を使用します。ビューの切り替えを有効にする方法は、使用しているルーターとフレームワークによって異なります。

document.startViewTransition(() => updateTheDOMSomehow());

これにより、デフォルトのトランジションが有効になります。このトランジションでは、新しいビューがフェードインするのと同時に古いビューがフェードアウトします。

何が起きているのでしょうか。document.startViewTransition() を呼び出すと、ブラウザは古いビューのスナップショットを取得します。次に、渡されたコールバック関数を呼び出し、DOM を新しいビューに更新します(ただし、まだ表示はされません)。コールバック関数が完了すると、ブラウザは新しいコンテンツへの移行を開始します。

// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
  updateTheDOMSomehow();
  return;
} else {
  // With a View Transition:
  document.startViewTransition(() => updateTheDOMSomehow());
}

移行をカスタマイズする

前の例で見たように、デフォルトのビュー トランジションでは、新しいビューがフェードインするのと同時に古いビューがフェードアウトします。ビュー トランジションによって生成された疑似要素をスタイル設定することで、サイトのスタイルに合わせてトランジションをカスタマイズできます。

::view-transition-old() で終了遷移を指定し、::view-transition-new() で開始遷移を指定できます。::view-transition-group() を使用して両方の値を指定することもできます。

この例では、古いビューは slide-out-to-left 遷移を使用してフェードアウトし、新しいビューは slide-in-from-right 遷移を使用してフェードインします。どちらも 200 ミリ秒の期間になります。

::view-transition-group(root){
  animation-duration: 200ms;
}

::view-transition-old(root) {
  animation-name: slide-out-to-left;
}

::view-transition-new(root) {
  animation-name: slide-in-from-right;
}

コンテキストに応じたさまざまなトランジション

ユーザーの操作に応じて異なるトランジションを設定することもできます。たとえば、ホームページのリンクをクリックすると新しいビューが右からスライドインする場合、ホームページに戻るリンクをクリックすると、ホームページのビューが左からスライドインすることが期待されます。

:active-view-transition-type() 疑似クラスを使用すると、さまざまなアニメーションを指定できます。

html:active-view-transition-type(forwards) {
  &::view-transition-old(root) {
    animation-name: slide-out-to-left;
  }

  &::view-transition-new(root) {
    animation-name: slide-in-from-right;
  }
}

document.startViewTransition() を呼び出すときに使用するビューの切り替え効果タイプを選択できます。

const direction = next === 'home' ? 'backwards' : 'forwards';

document.startViewTransition({
  update: updateTheDOMSomehow,
  types: [direction],
});

特定の要素の遷移

これまでは、ルート要素に遷移を適用して、ビュー全体を遷移させてきました。ビュー遷移を使用して、ページ内の特定の部分をアニメーション化することもできます。

たとえば、古いビューのコンテンツが新しいビューのコンテンツと一致している場合があります。コンテンツのタイトルや画像などです。たとえば、古いビューではサムネイル画像、新しいビューでは動画を表示することもできます。

まず、view-transition-name プロパティを使用して、どの要素を遷移させるかを指定する必要があります。ビューの切り替えを機能させるには、view-transition-name ごとに、document.startViewTransition() を呼び出す前に要素が 1 つだけ存在し、document.startViewTransition() のコールバックが完了した後に要素が 1 つだけ存在する必要があります。

この例では、アルバム アート、タイトル、アーティストを表示する音楽プレーヤーがあります。別のビューでは、同じコンテンツが再配置され、曲の歌詞が追加されています。

前の例では、古いビューと新しいビューに移行された要素がそれぞれ 1 つずつあり、セレクタも同じです。移行された要素は、サイズと位置の間を移動しているように見えます。ビューの移行されていない部分がフェードイン、フェードアウトします。

より複雑な例を見てみましょう。たとえば、ブログのホームページには各投稿の見出しと画像が表示され、ブログ投稿のフルページ ビューにも表示されます。ホームページから特定の投稿に移動する際に、タイトルと画像が新しい場所に移動してコンテキストを提供するように見せたい場合があります。

タイトルでこれを行うには、古いビューで一意であり、新しいビューのタイトル要素と共有され、新しいビューで一意である view-transition-name をタイトル要素に設定する必要があります。トップページには複数の見出しと画像があり、ユーザーがどれをクリックするかはわからないため、これは難しい問題です。

この問題を解決するには、次の 2 つの方法があります。ホームページの各投稿に一意の view-transition-name を追加し、各フルページ投稿でその名前を一致させることもできます。これらは投稿の ID を使用して生成できます。別の方法として、汎用 view-transition-name を使用することもできますが、ユーザーが投稿をクリックした後、document.startViewTransition() を呼び出す前にのみ適用してください。

遷移を設計する

ビューの切り替えは、ユーザーを誘導したり、ブランドやコンテキストのヒントを提供したりするために使用できる一連のツールです。サイトに適したトランジションを見つけるには、複数の手法を使用することをおすすめします。

目的の効果によっては、要素やアニメーションの調整が必要になることもあります。前の例では、スムーズなトランジションを実現するためにいくつかのスタイルが調整されています。

見出しには width: fit-content というルールが設定されています。これは、折り返されないテキスト(または古いビューと新しいビューで同じ折り返しが設定されているテキスト)を切り替える場合に便利なスタイルです。そうしないと、幅の異なる要素間でトランジションが行われ、トランジションがスムーズでなくなります。

また、画像のアスペクト比も古いビューと新しいビューで異なります。この例では、アニメーションと object-fit プロパティを変更して、遷移がスムーズに見えるようにしています。

prefers-reduced-motion を尊重する

ユーザーがモーションの低減をリクエストする一般的な理由として、ビューの切り替えなどで実現できる全画面アニメーションが、前庭性運動障害のあるユーザーに不快感を与えることが挙げられます。prefers-reduced-motion メディアクエリを使用してアニメーションを無効にできます。要素のつながりを伝えつつ、より控えめな代替アニメーションを提供することもできます。

@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

理解度を確認する

document.startViewTransition() が呼び出される前のビューを表す疑似要素の名前は何ですか?

::view-transition-previous
不正解です。
::view-transition-prior
不正解です。
::view-transition-old
正解です。
::view-transition-initial
不正解です。

View 遷移のデフォルトのアニメーションは何ですか?

新しい画像がフェードインすると同時に古い画像がフェードアウトする
正解です。
左から右にスライド
不正解です。
古い画像を白にフェードアウトし、新しい画像をフェードイン
不正解です。
スター ワイプ
不正解です。

ページのデフォルトの view-transition-name とは何ですか?

document
不正解です。
shadow-root
不正解です。
root
正解です。
body
不正解です。