sphinx 6644-768x585

MPA でページ遷移アニメーションを行う `@view-transition` CSS アットルール

View Transition API はページを遷移する際に簡単にアニメーションを追加できる API です。SPA では `document.startViewTransition()` メソッドを DOM が変更される前に呼び出すことでページ遷移アニメーションを追加できます。MPA の場合 CSS アットルール `@view-transition` を使用できます。SPA の場合と異なり、JavaScript を使用せずに CSS だけでアニメーションを追加できる点が特徴です。

View Transition API はページを遷移する際に簡単にアニメーションを追加できる API です。単一ページアプリケーション(SPA)においては document.startViewTransition() メソッドを DOM が変更される前に呼び出すことでページ遷移アニメーションを追加できます。

複数ページアプリケーション(MPA)においても同様のアニメーションを追加できるようにするために、CSS アットルール @view-transition を使用できます。SPA の場合と異なり、JavaScript を使用せずに CSS だけでアニメーションを追加できる点が特徴です。

ページ間の遷移アニメーションを有効にする

ページ空いた遷移アニメーションを有効にするには、@view-transition アットルールを使用します。@view-transition アットルールで navigation の値に auto を指定します。

Warning

ページ間のアニメーションが有効になるのは同一オリジンのページ間の遷移のみです。

@view-transition {
  navigation: auto;
}

これにより NavigationType のうち traverse, push, replace のいずれかに該当するナビゲーションが行われた際にページ遷移アニメーション発生します。push, replace の場合にはブラウザの UI のより発生したナビゲーションではなく、ユーザーの操作によって開始されたものである必要があります。

Warning

ページの読み込みに時間がかかりすぎる場合(Chrome では 4 秒以上)TimeoutError DOMException` が発生し、遷移アニメーションが無効になります。また一般にページ遷移アニメーションで十分な体験を提供するためにはページの読み込みが高速であることが必要でしょう。

実際に動作している動作を確認してみましょう。デフォルトではすべての要素に対してフェードイン/フェードアウトのアニメーションが適用されていることがわかります。

keyframes アニメーションの適用

:view-transition-old:view-transition-new という擬似要素を使用して、遷移前後の要素に対して異なるアニメーションを適用できます。

:view-transition-old は遷移前の要素に対して、:view-transition-new は遷移後の要素に対して適用されます。それぞれに @keyframes を指定して定義したアニメーションを適用します。

@keyframes slide-in {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}
 
@keyframes slide-out {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100%);
  }
}
 
@view-transition {
  navigation: auto;
}
 
::view-transition-old(root) {
  animation: slide-out 0.3s ease-out;
}
 
::view-transition-new(root) {
  animation: slide-in 0.3s ease-out;
}

実際に試してみると、スライドしながらページ遷移が行われることがわかります。

ビューの遷移タイプを渡す

ページによってどのような遷移アニメーションを適用するか指定するために、@view-transition アットルールに types プロパティを追加します。

@view-transition {
  navigation: auto;
  types: slide forward;
}

指定した遷移タイプは :active-view-transition-type 擬似クラスを使用してそれぞれの遷移タイプに対して異なるアニメーションを適用できます。

html:active-view-transition-type(forward) {
  &::view-transition-old(root) {
    animation: slide-out-to-right 0.3s ease-out;
  }
 
  &::view-transition-new(root) {
    animation: slide-in-from-left 0.3s ease-out;
  }
}
 
html:active-view-transition-type(back) {
  &::view-transition-old(root) {
    animation: slide-out-to-left 0.3s ease-out;
  }
 
  &::view-transition-new(root) {
    animation: slide-in-from-right 0.3s ease-out;
  }
}

遷移先の URL に応じて動的に遷移タイプを変更したい場合には、pageswappagereveal イベントを使用して、e.viewTransition.types の値を操作します。

pageswap イベントはページの最後のフレームがレンダリングされた後に発生します。pagereveal イベントはページが初期化された後の最初のレンダリングが行われる前に発生します。

e.viewTransition.typesSet のようなオブジェクトであり、.add(), .delete(), .clear() などのメソッドを使用して値を操作できます。

function determineTransitionType(from, entry) {
  // それぞれのページの遷移元と遷移先のページ番号を取得
  const fromPageNumber =
    from.url === undefined ? -1 : Number(from.url.split("page")[1]);
  const entryPageNumber =
    entry.url === null ? -1 : Number(entry.url.split("page")[1]);
 
  return fromPageNumber < entryPageNumber ? "back" : "forward";
}
 
// 新しいページが表示されるときに遷移タイプを決定する
document.addEventListener("pagereveal", (e) => {
  if (e.viewTransition) {
    const transitionType = determineTransitionType(
      navigation.activation.from,
      navigation.activation.entry,
    );
    e.viewTransition.types.add(transitionType);
  }
});

これによりページ番号が小さいページから大きなページへの遷移タイプは forward、大きなページから小さいページへの遷移タイプは back のように、リンク先によって異なる遷移アニメーションを適用できます。

特定の要素に遷移アニメーションを適用する

古いページと新しいページ両方に一意の view-transition-name が存在する場合、view-transition-name が一致する要素に対して遷移アニメーションが適用されます。もし同じページ内に複数の view-transition-name が存在する場合、ViewTransition.ready が拒否され遷移アニメーションが適用されません。

view-transition-name は CSS プロパティとして指定します。

.image {
  view-transition-name: image;
}

この例では画像がスケールしながらページ遷移が行われることがわかります。

特定の要素が出現するまで遷移アニメーションの開始を遅らせる

アニメーションを適用したい要素が JavaScript により動的に生成されるような場合には、遷移後の画面で要素が出現するまでアニメーションの開始を待つ必要があります。

<head> タグ内で以下の meta タグを追加することで、指定した id を持つ要素が出現するまでレンダリングをブロックできます。

<link rel="expect" blocking="render" href="#block" />

OS の設定に応じてアニメーションを無効化する

ユーザーが OS の「視差効果を減らす」などの設定を有効にしている場合、その設定を尊重してページ内のアニメーションを無効にすることが求められています。OS の設定によりアニメーションが無効にされているかどうかは prefers-reduced-motion メディアクエリを使用して判定できます。

もし prefers-reduced-motion メディアクエリによりアニメーションを無効にする場合、animation プロパティに none を指定してすべての View Transition アニメーションを無効にします。

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

まとめ

  • @view-transition アットルールを使用して MPA でページ遷移アニメーションを追加できる
  • :view-transition-old:view-transition-new を使用して遷移前後の要素に @keyframes アニメーションを適用できる
  • types プロパティを使用して遷移タイプに応じて異なるアニメーションを適用できる
  • pageswappagereveal イベントはそれぞれページ遷移前後に発生し、viewTransition プロパティを操作できる
  • view-transition-name プロパティを使用して特定の要素に遷移アニメーションを適用できる
  • prefers-reduced-motion メディアクエリを使用して OS の設定に応じてアニメーションを無効にできる

参考

記事の理解度チェック

以下の問題に答えて、記事の理解を深めましょう。

MPA でページ遷移アニメーションを行うために必要な CSS はどれか?

  • @view-transition { navigation: auto; }

    正解!

  • html { view-transition: auto; }

    もう一度考えてみましょう

  • ::view-transition { navigation: auto; }

    もう一度考えてみましょう

  • html { navigation: auto; }

    もう一度考えてみましょう


Contributors

> GitHub で修正を提案する
この記事をシェアする
はてなブックマークに追加

関連記事