タグアーカイブ アニメーション

クロスドキュメントビュー遷移の三大落とし穴と回避策

クロスドキュメントビュー遷移の三大落とし穴と回避策

クロスドキュメントビュー遷移(Cross-Document View Transitions)は、MPA(マルチページアプリケーション)でありながらSPAのようなスムーズなページ遷移アニメーションを実現するブラウザAPIだ。ReactやAstroといったフレームワークは不要で、HTMLページ間のリンク遷移にブラウザが自動的にアニメーションを付与する。

しかし、この機能の導入にはいくつかの厄介な落とし穴が潜んでいる。CSS-Tricksの記事によれば、著者は実装に丸一日を費やし、何も動作しない状態からデバッグを繰り返したという。ネット上には古い情報や誤解を招くチュートリアルが溢れており、仕様自体も短期間で変更されている。

本記事では、実際の開発現場で遭遇する3つの主要な問題(非推奨metaタグ、4秒タイムアウト、画像の歪み)とその解決策を解説する。加えて、遷移ライフサイクルを制御する2つのイベントについても触れる。

非推奨となったmetaタグの罠

非推奨となったmetaタグの罠

多くの開発者が最初にハマるのが、古いチュートリアルに記載された<meta>タグによるオプトイン方式だ。この方式は既に非推奨であり、現在のブラウザでは完全に無視される。

非推奨(Before)
<meta name=”view-transition” content=”same-origin”>
ブラウザが沈黙し、何も動作しない
推奨(After)
@view-transition { navigation: auto; }
CSSでオプトイン、条件付き制御も可能

この比較が示すように、現在の正しい実装方法はCSSの@規則を使用することだ。Chrome 111でmetaタグが導入された後、Chrome 126前後でCSSベースの方式に置き換えられた。非推奨の警告はDevToolsに表示されず、古いコードは静かに動作しなくなる。

なぜCSS方式に移行したのか

metaタグ方式の最大の欠点は、ページ全体でオンかオフかの二択しかできなかったことだ。CSS方式ではメディアクエリや@supportsと組み合わせて、条件付きのオプトインが可能になる。

@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

@media (min-width: 768px) {
  @view-transition {
    navigation: auto;
  }
}

このアプローチにより、アニメーションに敏感なユーザーへの配慮や、モバイルデバイスでのパフォーマンス最適化が容易になった。CSSにオプトインが統合されたことで、既存のスタイル管理フローと一貫性を持って扱える点も大きい。

両方のページでオプトインが必須

もう一つの重要なポイントは、遷移を機能させるには遷移元と遷移先の両方のページで@view-transitionが宣言されている必要があることだ。片方だけでは何も起きない。これは意図的な設計で、404ページやログインリダイレクトなど遷移をスキップしたいページを柔軟に制御できる。

なお、navigation: autoはユーザーがリンクをクリックするかブラウザの戻るボタンを押した場合のみ発動する。window.location.hrefによるプログラム的な遷移や、クロスオリジンのリンク、POSTリクエストでは動作しない。この保守的な設計は、決済処理などの重要な操作に意図しないアニメーションが混入するのを防ぐためだ。

4秒タイムアウトが遷移を静かに殺す

4秒タイムアウトが遷移を静かに殺す

ビュー遷移の実装で最もデバッグが難しい問題が、ハードコードされた4秒のタイムアウトだ。新しいページが4秒以内にレンダリング可能な状態に達しないと、遷移アニメーションは何の通知もなくキャンセルされ、通常のページ読み込みのように切り替わる。

タイムアウト発生 4秒経過 ブラウザ 遷移を破棄 エラー表示なし
※TTFB(サーバー応答待ち2秒 + レンダリング2.5秒でアウト)

この問題が厄介なのは、ローカル開発環境ではまず発生しないことだ。devサーバーは80msで応答するため遷移は完璧に動作するが、本番環境でサーバーレス関数のコールドスタートやCDNキャッシュミスが発生すると、最初のクリックで遷移が無効化される。

pagerevealイベントでタイムアウトを捕捉する

タイムアウトの発生を検知するには、pagerevealイベントとviewTransition.finishedプロミスを使用する。以下のコードをページに組み込めば、遷移が失敗した際にコンソールで確認できる。

window.addEventListener("pagereveal", (event) => {
  if (!event.viewTransition) {
    console.log("ビュー遷移なし");
    return;
  }
  event.viewTransition.finished
    .then(() => console.log("遷移完了 ✅"))
    .catch((err) => {
      console.error("遷移中断", err.name, err.message);
    });
});

このリスナーを早期にセットアップしておけば、本番環境でのデバッグが格段に容易になる。pageswapイベントでも同様に遷移元ページ側でタイムアウトを捕捉可能だ。

実用的な対策

タイムアウト対策の基本はページの読み込み速度改善だが、より実践的なアプローチとしてrel="expect"属性の活用がある。

<link rel="expect" href="#hero" blocking="render">

これはブラウザに「#hero要素がDOMに存在するまでページをレンダリング可能と見なさない」と指示するものだ。一見するとパフォーマンスを悪化させるように思えるが、ビュー遷移においては重要なコンテンツが揃ってからスナップショットを取得するため、中途半端な状態での遷移を防げる。

タイムアウトのクロックはナビゲーション開始時からカウントされるため、サーバー応答時間(TTFB)も含まれる点に注意が必要だ。サーバーが2秒かけて応答し、さらに2.5秒かけてレンダリングする場合、個別には遅く感じなくても合計で4.5秒となりタイムアウトに引っかかる。

画像が歪む根本原因と解決策

画像が歪む根本原因と解決策

ビュー遷移の実装で視覚的に最も目立つ問題が、アスペクト比の異なる画像間の遷移で発生する歪みだ。サムネイルからヒーロー画像への遷移で、画像が引き伸ばされて見苦しくなる現象は多くの開発者が経験する。

CSS-Tricksの著者は、この問題の原因を特定するのにかなりの時間を費やしたという。根本的な原因は、ブラウザが遷移中に<img>要素そのものをアニメーションさせるのではなく、古い状態と新しい状態のスクリーンショット(ビットマップ)を取得し、それらを変形させることにある。

変形した状態(Before)
サムネイル
object-fit: cover → 無効化
ビットマップが引き伸ばされる
整形された状態(After)
::view-transition-old(hero-img),
::view-transition-new(hero-img) {
object-fit: cover;
overflow: hidden;
}

上記の図が示すように、解決策は疑似要素::view-transition-old::view-transition-newに対してobject-fit: coverを適用することだ。これにより、スナップショット画像がアスペクト比を維持したまま切り抜かれるようになる。

疑似要素ツリーの構造を理解する

ビュー遷移が発生すると、ブラウザは内部的に次のような疑似要素ツリーを生成する。

::view-transition
└── ::view-transition-group(hero-img)
    ├── ::view-transition-old(hero-img)
    └── ::view-transition-new(hero-img)

::view-transition-groupが古い寸法から新しい寸法へアニメーションするコンテナとして機能し、その中のoldnewが実際のスナップショットを保持する。デフォルトではこれらの疑似要素にobject-fit: fillが適用されており、これが歪みの原因となる。

アスペクト比が大きく異なるケースでは、object-positionで切り抜き位置を調整することも有効だ。

::view-transition-old(hero-img) {
  object-fit: cover;
  object-position: center center;
}
::view-transition-new(hero-img) {
  object-fit: cover;
  object-position: center top;
}

このコードでは、新しいヒーロー画像の上部を優先的に表示しつつ、遷移中の歪みを防ぐことができる。CSS-Tricksの著者も指摘するように、object-fit: coverはほぼ全ての画像遷移で必要になる設定であり、デフォルトがfillであることは実用上の大きな障壁となっている。

pageswapとpagerevealによるライフサイクル制御

pageswapとpagerevealによるライフサイクル制御

クロスドキュメントビュー遷移では、遷移元と遷移先のページがJavaScriptで直接通信できないという制約がある。この問題を解決するのがpageswappagerevealの2つのイベントだ。

遷移元ページ pageswap発火 要素に名前を付与
event.activation.entry.url で遷移先を特定可能
遷移先ページ pagereveal発火 遷移元の情報を取得
navigation.activation.from.url で遷移元を特定可能

このイベントペアにより、開発者は遷移の両端で状態を制御できる。pageswapは遷移元ページがスナップショットされる直前に発火し、event.activation.entry.urlでユーザーがどこへ向かっているかを知ることができる。

イベントハンドラの実装パターン

これらのイベントを使用する際の重要なポイントは、必ずevent.viewTransitionの存在確認を行うことだ。pagerevealはビュー遷移がない場合も含め、全てのナビゲーションで発火する。

window.addEventListener("pagereveal", (event) => {
  if (!event.viewTransition) return;
  
  event.viewTransition.finished.then(() => {
    // 遷移完了後のクリーンアップ
  }).catch((err) => {
    // タイムアウト等のエラー処理
  });
});

CSS-Tricksの記事では、商品一覧ページから商品詳細ページへの遷移において、pageswapでクリックされた商品カードだけにview-transition-nameを動的に付与するパターンが紹介されている。この動的な名前付けは、数十から数百の要素があるページでのスケーラビリティ問題を解決する重要な手法だ。

この記事のポイント

  • metaタグ方式は非推奨。CSSの@view-transition { navigation: auto; }を使用する
  • 4秒のタイムアウトはTTFBを含む総時間で判定され、pagerevealイベントで捕捉可能
  • 画像歪みは疑似要素::view-transition-old/newへのobject-fit: cover適用で解決
  • pageswappagerevealの2つのイベントが遷移全体のライフサイクルを制御する
CSSだけでApple Vision Pro風スクロールアニメーションを再現する高度なテクニック

CSSだけでApple Vision Pro風スクロールアニメーションを再現する高度なテクニック

Appleの製品ページで多用される、スクロールに連動したダイナミックなアニメーションは、多くのWeb制作者にインスピレーションを与えてきた。特にVision Proの紹介ページで見られる、デバイスが分解されながら迫ってくるような演出は、技術的にも非常に洗練されている。

これまでこうした演出の多くはJavaScriptを用いて制御されていたが、最新のCSS機能を駆使することで、スクリプトなしでの再現が可能になりつつある。CSS-Tricksの記事では、スクロール駆動アニメーション(Scroll-driven Animations)を活用し、Apple風の演出をCSSだけで構築する手法が提案された。

本記事では、その実装の核心となる「パーツの分解」と「デバイスの反転」という2つのステージを、最新のCSSプロパティを用いてどのように制御するのかを深掘りしていく。パフォーマンスとレスポンシブ対応を両立させるための、具体的な計算式や構造の設計についても詳しく見ていこう。

Appleのスクロール演出を構成する2つのステージ

Appleのスクロール演出を構成する2つのステージ

Vision Proのアニメーションを再現するためには、まずその動きを論理的に分解する必要がある。CSS-Tricksの分析によれば、この演出は大きく分けて2つの段階で構成されているという。

ステージ1 ハードウェアの分解表示

最初の段階では、デバイスの底部から3つの主要な電子部品が順番に浮き上がってくる。それぞれのコンポーネントは、他の部品を挟み込むように配置された2枚の画像で構成されている。これにより、部品が重なり合いながらも奥行きを感じさせる、立体的な「爆発図」のような効果が生まれる。

この視覚効果のポイントは、透明な領域を含む複数のレイヤーが、スクロールに合わせて異なる速度やタイミングで移動することだ。最前面と最後面に配置された画像が、中間にある部品を包み込むように動くことで、単なる平面の移動ではない3D的な深みが表現されている。

ステージ2 接眼レンズへのフリップアップ

部品の分解が終わると、次にデバイス全体が滑らかに回転し、接眼レンズ(アイピース)が見える状態へと変化する。Appleの公式サイトでは、この部分はJavaScriptで動画の再生位置をスクロール量に合わせて制御することで実現されている。

これをCSSだけで再現する場合、動画ファイルの代わりに大量の静止画を高速で切り替える手法が検討される。スクロールというユーザーの入力に対して、パラパラ漫画のように画像を差し替えていくことで、動画と同等の滑らかな回転アニメーションを作り出すアプローチだ。

Gridレイアウトによる要素の重ね合わせと配置

Gridレイアウトによる要素の重ね合わせと配置

アニメーションを実装する前の準備として、複数の部品画像を正確に重ね合わせる必要がある。従来は position: absolute を多用していたが、これでは要素が通常の文書フローから外れてしまい、レスポンシブ対応やスクロール位置の管理が複雑になるという課題があった。

CSS-Tricksの筆者は、この問題の解決策として display: grid の活用を挙げている。親要素を1カラム・1行のグリッドに設定し、すべての部品画像を同じグリッドエリア(grid-area: 1 / 1 / 2 / 2)に割り当てることで、文書フローを維持したまま完璧な重ね合わせを実現できる。

Gridによる重ね合わせの概念図
背面パーツ
中央パーツ
前面パーツ
各要素が同じエリアに重なりつつ、個別に移動可能

このデモのように、グリッドを使うことで要素の順序(z-index)を保ちながら、個々のパーツに自由なアニメーションを適用できる土台が整う。また、各画像に background-size: cover を適用することで、アスペクト比を維持したまま画面幅に合わせることも容易になる。

StickyとView Timelineによるスクロール制御

StickyとView Timelineによるスクロール制御

スクロールに応じてアニメーションを動かす際、最も重要なのが「要素が画面内の特定の場所に留まり続けること」と「要素の表示状態を検知すること」の2点だ。これを実現するのが position: stickyview-timeline プロパティである。

要素を画面に固定するStickyの役割

アニメーションが実行されている間、対象のデバイスが画面外に流れていってしまっては意味がない。そこで、アニメーション全体を包むコンテナ要素に十分な高さを設定し、中のデバイス要素に position: sticky; top: 0; を指定する。これにより、ユーザーがスクロールしている間、デバイスは画面上部に固定され、アニメーションの変化だけが視覚的に伝わるようになる。

View Timelineによる実行タイミングの最適化

従来、スクロールアニメーションの開始位置を特定するには、JavaScriptでスクロール量を監視し、要素のオフセットを計算する必要があった。しかし、最新のCSSでは view-timeline-name を定義するだけで、その要素がビューポート(画面)に入ってきたことをトリガーにアニメーションを開始できる。

CSS-Tricksの記事では、scroll-timeline ではなく view-timeline を選択した理由として、レスポンシブ性の向上を挙げている。ページの総高さに依存する scroll-timeline よりも、要素自体の表示状態に基づく view-timeline の方が、画面サイズが変わっても正確なタイミングでアニメーションを開始できるからだ。

レスポンシブ対応のための動的な高さ計算

レスポンシブ対応のための動的な高さ計算

アニメーションの移動量を固定値(px)で指定すると、画面サイズが小さいデバイスではパーツが画面外に飛び出したり、逆に移動が足りなかったりする問題が発生する。これを防ぐために、数学的なアプローチが必要となる。

デバイスの画像サイズが 960px × 608px である場合、現在の表示幅に基づいた動的な高さを calc() 関数で算出できる。具体的には、以下の計算式を用いることで、画像の比率を維持した高さを取得し、それを移動量の基準にする手法だ。

:root {
  --stage2-height: calc(min(100vw, 960px) * 608 / 960);
}

@media screen and (max-height: 608px) {
  :root {
    --stage2-height: 100vh;
  }
}

この計算式により、ブラウザの幅が狭いときは 100vw に基づいた高さが計算され、画面の高さが極端に低い場合は 100vh が優先される。こうして得られた --stage2-height 変数を translate プロパティに適用することで、どのような画面サイズでもパーツが適切な位置まで移動し、重なりを維持できるようになる。

パラパラ漫画方式による「動画風」アニメーション

パラパラ漫画方式による「動画風」アニメーション

前述の通り、CSSだけで動画のフレームを制御することはできない。そこで、ステージ2のフリップアップ演出では、背景画像を高速で切り替える手法が採用された。これは、キーフレームアニメーションの中で background-image を順番に指定していく方法だ。

具体的には、0%から100%までの進行度に合わせて、数十枚の静止画(00011.jpg、00013.jpg…)を切り替えていく。この際、パフォーマンスを向上させるために、HTMLの <link rel="preload" as="image"> タグを使用して、すべての画像を事前に読み込んでおくことが推奨されている。これにより、スクロール時の画像のチラつきや遅延を防ぐことができる。

画像切り替えによる回転演出(Before / After)
スクロール開始(画像001)
スクロール中(画像060:回転完了)
● ●
※スクロール位置に応じて背景画像が差し替わり、擬似的に3D回転を表現する

この手法のデメリットは、1つの動画ファイルの代わりに大量の静止画をダウンロードする必要がある点だ。CSS-Tricksの筆者は、フレーム数を半分に間引くことでファイル数を削減しつつ、視覚的な滑らかさを維持する工夫を凝らしている。実運用では、画像の最適化やスプライト画像化などのさらなる対策が有効だろう。

Animation Rangeによる精緻なタイミング調整

Animation Rangeによる精緻なタイミング調整

view-timeline を使うだけでは、アニメーションが開始・終了するタイミングを細かく制御できない場合がある。そこで役立つのが animation-range プロパティだ。これは、要素がビューポートのどの位置に来たときにアニメーションを開始し、どこで終了するかを定義するものだ。

例えば、部品の分解アニメーション(ステージ1)では animation-range: contain cover; が使用された。これは、要素が完全に画面内に入ってから(contain)アニメーションを開始し、画面から消え去るまで(cover)継続することを意味する。一方、回転アニメーション(ステージ2)では、画面から消える前に動きを完結させる必要があるため、animation-range: cover 10% contain; のような指定で調整が行われている。

このように、スクロール量という「時間軸」に対して、アニメーションの「区間」を定義することで、JavaScriptを使わずとも極めて精度の高い演出制御が可能になる。これは、現代のCSSにおける大きな進化の一つだ。

独自の分析:CSS主導のアニメーションがもたらす変化

独自の分析:CSS主導のアニメーションがもたらす変化

今回紹介した手法は、単に「Appleの真似ができる」という以上の意味を持っている。最大の利点は、ブラウザのメインスレッドをJavaScriptの計算から解放できることにある。スクロール駆動アニメーションは、ブラウザのコンポジタースレッドで処理されるため、ページの読み込みや他の処理が重い状況でも、カクつきの少ないスムーズな動きを提供できる。

一方で、実務上の課題も残されている。大量の画像を切り替える手法は、LCP(Largest Contentful Paint)などのパフォーマンス指標に悪影響を与える可能性があるからだ。また、現時点ではFirefoxがこのCSS機能に完全対応していないため、フォールバック(代替表示)の用意が欠かせない。

しかし、これまで「実装コストが高すぎる」と諦めていた高度な演出が、CSS数行で記述できるようになった意義は大きい。今後は、動画ファイルとCSSアニメーションをより高度に組み合わせた、ハイブリッドな実装が主流になっていくのではないかと推測される。

この記事のポイント

  • Apple風のスクロール演出は、部品の分解と回転という2つのステージに分けて考える
  • display: grid を使うことで、要素の重ね合わせとレスポンシブな配置を両立できる
  • view-timelineposition: sticky の組み合わせが、スクロール連動の鍵となる
  • 大量の画像をCSSで切り替える際は、preload による事前読み込みが不可欠だ
  • animation-range を活用することで、JSなしでも精緻な実行タイミングの制御が可能になる
View Transitions APIでサイト体験を劇的に変える!CSSだけで実現する7つのページ遷移レシピ

View Transitions APIでサイト体験を劇的に変える!CSSだけで実現する7つのページ遷移レシピ

Webサイトのページを切り替える際、画面が瞬時にパッと切り替わるのではなく、モバイルアプリのような滑らかなアニメーションを伴う手法が注目されている。これを実現するのが「View Transitions API」だ。複雑なJavaScriptライブラリを使わずに、ブラウザの標準機能とCSSだけで高度な遷移エフェクトを実装できる。

View Transitions APIは主要なブラウザでのサポートが進み、実用的な段階に入った。特にマルチページアプリケーション(MPA)でも、ページ間の連続性を保った演出が可能になった点は大きい。ユーザー体験を向上させるための強力な武器になるだろう。

本記事では、CSS-Tricksの記事を基に、すぐに試せる7つのアニメーションレシピを紹介する。基本的なセットアップから、ぼかしや3D回転を組み合わせた応用例まで、その仕組みを詳しく解説していく。技術的なハードルは低いため、最新のWeb制作トレンドを取り入れたいエンジニアやデザイナーにとって有益な情報となるはずだ。

View Transitions APIの基本設定と導入のポイント

View Transitions APIの基本設定と導入のポイント

View Transitions APIを利用するには、まずブラウザに対して「このサイトでページ遷移のアニメーションを有効にする」という宣言が必要だ。これを「オプトイン(利用選択)」と呼ぶ。CSSの @view-transition アットルールを使い、遷移元と遷移先の両方のページで設定を行う必要がある。

@view-transitionルールでのオプトイン

最も基本的な設定は、CSSに数行のコードを追加するだけで完了する。共通のCSSファイルに記述しておくことで、サイト全体に適用できる。 navigation: auto を指定すると、通常のリンク移動時にブラウザが自動的にトランジションを実行するようになる。

@view-transition {
  navigation: auto;
}

この設定だけで、ブラウザのデフォルトである「クロスフェード(前の画面が消えながら次の画面が浮き上がる)」が適用される。さらに特定の名前(タイプ)を付けることで、ページの種類ごとに異なるアニメーションを使い分けることも可能だ。

ユーザーの好みに配慮したアクセシビリティ対応

アニメーションを実装する上で忘れてはならないのが、アクセシビリティへの配慮だ。OSの設定で「視覚効果を減らす」を選択しているユーザーに対しては、激しい動きを控えるべきだ。これを判定するのが prefers-reduced-motion メディアクエリである。

「動きを減らす」設定が無効(no-preference)の場合のみアニメーションを有効にする記述が推奨される。これにより、すべてのユーザーが快適にサイトを閲覧できる環境を整えられる。技術的な新しさを追求するだけでなく、こうした配慮をセットで行うのがプロの仕事だ。

@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
    types: my-transition;
  }
}

視覚効果で魅せるフェードとワイプのレシピ

視覚効果で魅せるフェードとワイプのレシピ

ここからは具体的なアニメーションレシピを見ていこう。まずは定番のフェード効果をアレンジしたものや、画面を拭き取るようなワイプ効果だ。これらは汎用性が高く、どんなジャンルのサイトにも馴染みやすい。

ぼかしを活用したPixelate dissolve

単なるフェードではなく、画面全体をぼかしながら切り替えるのが「Pixelate dissolve(ピクセレート・ディゾルブ)」だ。CSSの filter: blur() プロパティを使用する。古いページがぼやけて消えていき、新しいページがぼやけた状態から鮮明に現れる演出だ。

t=0% (開始・古いページ)
元のコンテンツ(鮮明)
t=50% (ぼかしながらクロスフェード中)
遷移中(ぼやけて溶け合う)
t=100% (完了・新しいページ)
新しいコンテンツ(鮮明)

このアニメーションは実際には約1.4秒かけて連続的に行われる。中間状態の filter:blur(6px)opacity:0.6 がスムーズに変化することで、画面全体が一度ぼやけてから鮮明な新画面が立ち上がる演出になる。短く設定すればキビキビとしたモダンな操作感、長くすればゆったりとした高級感のある印象を与えられる。

clip-pathで実現する上下左右のWipe効果

「Wipe(ワイプ)」は、画面をスライドさせて覆い隠すような効果だ。これには clip-path プロパティの inset() 関数を利用する。 inset() は要素の表示領域を上下左右からの距離で指定する仕組みで、この数値を 0% から 100% へ動かすことで、コンテンツを削り取るような動きを作れる。

例えば「Wipe up(上方向へのワイプ)」なら、古いページの表示領域を下から上へ 100% 削り、新しいページを上から下へ 0% に戻していく。 clip-path を使うメリットは、実際のレイアウトを崩さずに表示領域だけを制御できる点にある。非常にパフォーマンスが良く、滑らかな動きを実現できる。

ダイナミックな動きを作る回転とプッシュの演出

ダイナミックな動きを作る回転とプッシュの演出

次に、より動きの大きいダイナミックな演出を紹介する。これらはユーザーの目を引きやすいため、ポートフォリオサイトやキャンペーンページなど、個性を出したい場面で有効だ。 transform プロパティを駆使して、空間的な広がりを演出する。

遊び心のあるRotate in-out

「Rotate in-out(回転イン・アウト)」は、ページが回転しながら縮小して消え、新しいページが逆回転しながら拡大して現れるエフェクトだ。 scale(0)rotate(180deg) を組み合わせる。実用性は限られるかもしれないが、View Transitionsの表現力の高さを示す良い例だ。

t=0% (開始・元ページ)
元コンテンツ
t=50% (切替の瞬間・縮小して回転中)
回転中
t=100% (完了・新ページ)
新コンテンツ

このアニメーションは実際には約1秒かけて連続的に行われる。中間状態では transform:scale(0.3) rotate(90deg)opacity:0.4 によって元ページが縮小しながら回転して消え、その直後に新ページが逆方向から拡大して現れる。transform-origin: center を指定して画面中央を軸に回転させるのがポイントだ。また、回転角度を大きくしすぎるとユーザーが酔ってしまう可能性があるため、180度程度に抑えておくのが無難だ。

画面の隅から現れるDiagonal push

「Diagonal push(斜めプッシュ)」は、古いページを斜め方向に押し出し、新しいページを逆の斜め方向から滑り込ませる演出だ。 translate(-100%, -100%) のように X軸 と Y軸 の両方を同時に動かすことで斜めの移動を実現する。

この演出は、スライド資料を切り替えるような感覚をユーザーに与える。移動の軌跡に合わせて opacity (不透明度)を変化させると、より自然で洗練された印象になる。 ease (緩急)の指定を工夫することで、重厚感のある動きから軽快な動きまで調整可能だ。

形状と奥行きを活かした高度なトランジション

形状と奥行きを活かした高度なトランジション

最後に、より高度な視覚効果を紹介する。これらは clip-path の応用や 3D変形 を使用しており、実装には少しコツが必要だが、その分インパクトは非常に大きい。ブラウザが自動的に生成するスナップショットをどのように加工するかが鍵となる。

円形に広がるCircle wipe-out

「Circle wipe-out(サークル・ワイプ)」は、画面中央から円形に新しいページが広がっていく演出だ。映画のシーン切り替えなどで見かける手法である。 clip-path: circle() を使い、半径を 0% から 150% まで拡大させることで、画面全体を覆い尽くす動きを作る。

このレシピの面白い点は、背景色が同じページ間での遷移だ。背景が変わらずにコンテンツだけが円形に浮き上がってくるように見えるため、非常にシームレスな体験を提供できる。中心点は at 50% 50% だけでなく、クリックした位置に合わせて動的に変更するような応用も考えられる。

幕が開くようなCurtain reveal

「Curtain reveal(カーテン・リビール)」は、舞台の幕が左右に開くような動きだ。これも clip-path: inset() を使用するが、左右の値を 50% から 0% へと変化させる点が特徴だ。画面中央から左右に向かって新しいページが露出していく様子は、新しい体験の始まりを予感させる。

t=0% (幕が完全に閉じている)
舞台の中身
t=50% (幕が左右に開いて中身が半分見える)
舞台の中身
t=100% (幕が完全に開いて全体が見える)
舞台の中身がフル表示
幕(左右から覆う部分)  新ページのコンテンツ

このアニメーションは実際には約0.8秒かけて連続的に行われる。clip-path: inset(0 50% 0 50%) から inset(0 0 0 0) へと値が変化することで、左右から幕が引かれて中央のコンテンツが露出していく。実際のView Transitionsでは、::view-transition-new(root) に対してこのクリッピングアニメーションを適用することで、滑らかなカーテン効果が実現する。

3D空間でカードがめくれる3D flip

最もインパクトがあるのが「3D flip(3Dフリップ)」だ。ページ全体を一枚のカードに見立て、 Y軸 を中心に回転させて裏返すような演出を行う。 rotateY(90deg) でページを真横に向け、その瞬間に新しいページと入れ替えて 0deg に戻していく。

この演出を成功させるには、 perspective (遠近感)の設定が重要だ。奥行きを感じさせる数値を指定することで、平面的な画面の中に立体的な空間が生まれる。ただし、非常に目立つエフェクトなので、使いどころを慎重に選ぶ必要があるだろう。

実務でView Transitionsを導入する際の注意点

実務でView Transitionsを導入する際の注意点

View Transitions APIは非常に強力だが、実務に導入する際にはいくつか考慮すべき点がある。単にコードをコピーするだけでなく、プロジェクトの要件に合わせた最適化が必要だ。ここでは、技術的な側面とユーザー体験の両面から、筆者の見解を交えて解説する。

ブラウザサポートとフォールバックの考え方

View Transitions APIは現在、ChromeやEdgeなどのChromium系ブラウザで先行して実装され、SafariやFirefoxでも順次対応が進んでいる。しかし、すべてのユーザーが最新ブラウザを使っているわけではない。そのため、「アニメーションが動かなくてもコンテンツは正しく表示される」というプログレッシブ・エンハンスメントの考え方が不可欠だ。

幸いなことに、View Transitions APIは「対応していないブラウザでは単にアニメーションが無視されるだけ」という特性を持っている。特別なJavaScriptによる条件分岐を書かなくても、基本的には安全に導入できる。ただし、アニメーションがあることを前提とした複雑なUI設計は避けるべきだ。

パフォーマンスへの影響と最適化

トランジション実行中、ブラウザは画面のスナップショット(画像のようなもの)を作成し、それをアニメーションさせている。そのため、非常に高解像度な画像が大量にあるページや、複雑なDOM構造を持つページでは、一瞬の動作の重さを感じることがあるかもしれない。

対策としては、 will-change プロパティを適切に使ってブラウザに最適化を促すことや、アニメーションさせる要素を view-transition-name で限定することが有効だ。画面全体(root)を動かすのではなく、ヘッダーやロゴなどの共通要素を固定し、中身のコンテンツだけを動かすようにすると、より軽快で自然な遷移になる。

この記事のポイント

  • View Transitions APIはCSSだけでモバイルアプリのような滑らかなページ遷移を実現する
  • @view-transition ルールの navigation: auto 設定でMPAでも簡単に導入できる
  • clip-pathfilter を組み合わせることで、ぼかしやワイプなど多様な演出が可能になる
  • prefers-reduced-motion を使い、動きを好まないユーザーへの配慮を忘れない
  • 対応ブラウザ以外では通常の遷移になるため、プログレッシブ・エンハンスメントとして導入しやすい