Inside cmn!

山本 慎二

今更ながらView Transition API入門 ライブラリなしでページ遷移アニメーションを実現—基本から swup.js との比較まで

UI/UXプログラミングTips

この記事は誰向け?

  • フロントエンドエンジニアの方: ページ遷移アニメーションをいつもライブラリに頼ってしまっていて、View Transition APIは気になりつつもキャッチアップできていなかった方
  • ディレクターの方: 「ページ遷移のアニメーションをいい感じにしたい」という提案が、実装コストの面で二の足を踏んでいた方

View Transition APIとは

View Transition APIとは、ページ内・ページ間のDOM変化をなめらかにアニメーションするための、ブラウザネイティブなAPIです。
DEMOページはこちら

仕組みを簡単に説明すると

  1. 変化前の画面をスナップショットとして撮る
  2. DOMを更新する
  3. 変化後の画面もスナップショットとして撮る
  4. 2枚のスナップショットの間をブラウザが自動でアニメーションする

これだけです。ライブラリがやっていた「新旧の画面を重ねてフェードさせる」処理を、ブラウザが丸ごとやってくれるようになったイメージですね。

ブラウザサポート状況

View Transition APIはもともとChromeのみで使えるAPIという印象が強かったのですが、2025年10月14日にFirefox 144がリリースされ、SPA向け機能は主要3ブラウザ全体での対応が完了しました。

Same-document(SPA向け)

ブラウザ サポート開始
Chrome / Edge 111以降
Safari 18以降
Firefox 144以降(2025年10月〜)

基本的なSPA向け機能に関しては2025年10月14日付けでBaseline Newly Available(主要3ブラウザすべてでサポート済み)のステータスになりました。
これは2023年3月にGoogle Chrome先行でサポートされてから、おおよそ2年半の間にほぼ実務投入できる水準になったということで、これまでのWebの歴史から見てもかなり早い部類のようです。

Cross-document(MPA・通常のページ遷移向け)

ブラウザ サポート開始
Chrome / Edge 126以降 ✅
Safari 18.2以降 ✅
Firefox 未対応 ⚠️

MPAでのページ間トランジションについては、Firefoxがまだ未対応です。(非対応ブラウザでは普通にページ遷移するだけで、エラーにはなりません。)

ライブラリとの違い

swup.jsやBarba.jsなどのライブラリは、ページ遷移をフックしてアニメーションを挿入するという仕組みで、ブラウザのネイティブ遷移を置き換えていました。それに対してView Transition APIは、ブラウザ自身がアニメーションを担います。

結果として、

  • ライブラリのインストールが不要
  • バンドルサイズが増えない
  • スクロール位置のリセットなど、ブラウザが本来やるべきことをブラウザが自動でやってくれる

というメリットがあります。とはいえ、ライブラリにも得意な場面があります。その比較は後述します。

書き方

Same-document(SPA向け)の基本

JavaScriptでDOM操作をしているページで試せる、もっとも基本的な使い方です。

実装はとてもシンプルで、DOM更新処理を document.startViewTransition() の引数として渡すだけです。

CSS

.panel {
  display: none;
}
.panel.is-open {
  display: block;
}

JS

// View Transition APIを使う書き方
function showDetail() {
  document.startViewTransition(() => {
    // 遷移後のDOMの変更を記述
    document.querySelector('.panel').classList.add('is-open');
  });
}

これだけでデフォルトのクロスフェードアニメーションが付きます。驚くほどシンプルですよね。

プログレッシブエンハンスメントとして書く

もし非対応ブラウザも考慮するなら、サポートチェックを1行加えるだけで安全に動きます。

JS

function updateUI(callback) {
  if (!document.startViewTransition) {
    callback(); // 非対応ブラウザは従来通り
    return;
  }
  document.startViewTransition(callback);
}

function showDetail(){
  // 遷移後のDOMの変更を記述
  document.querySelector('.panel').classList.add('is-open');
}

updateUI(showDetail); //ボタンクリックなどにバインド

CSSでアニメーションをカスタマイズする

デフォルトのクロスフェードをカスタマイズしたい場合は、View Transition APIが提供する専用の擬似要素をCSSで記述します。
3-1からJSの記述変更は必要ありません!

CSS

/* 遷移前の画面(古いスナップショット) */
::view-transition-old(root) {
  animation: slide-out 0.3s ease-in;
}

/* 遷移後の画面(新しいスナップショット) */
::view-transition-new(root) {
  animation: slide-in 0.3s ease-out;
}

@keyframes slide-out {
  to {
    transform: translateX(-100%);
    opacity: 0;
  }
}

@keyframes slide-in {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
}

::view-transition-old::view-transition-new が新旧スナップショットに対応しています。この2つをCSSアニメーションで制御するだけで、スライドやフェード、スケールなど自由な演出が作れます。

view-transition-name で要素を個別にアニメーションさせる

特定の要素に view-transition-name を指定すると、その要素だけを個別にアニメーションさせられます。サムネイルが詳細表示にぬるっと拡大するような、iOSアプリでよく見るアニメーションがこれで実現できます。

CSS

/* アニメーションさせたい要素に名前をつける */
.product-thumbnail {
  view-transition-name: hero-image;
}

名前をつけた要素は、変化前後の位置・サイズの間をブラウザが自動で補間してくれます。これだけで、クリックした画像が詳細ページの大きな画像へとなめらかに変形する演出が完成します。

注意点:view-transition-name はページ内でユニークな値にする
同じ view-transition-name を複数の要素に付与すると正しく動作しません。リスト要素など動的に生成される要素の場合は、JavaScriptで一意の値を割り当てるか、2025年から使えるようになった view-transition-name: match-element を活用しましょう。

CSS

/* match-element:要素が自動でユニークな名前を持つ */
.list-item {
  view-transition-name: match-element;
}

view-transition-name: match-element はブラウザが「変更前の要素」と「変更後の要素」を同じオブジェクト(同一の参照)として認識できる場合は利用できますが、変更後の要素が動的に生成されたりするケースでは単なるクロスフェードとなってしまうため、その場合は動的にview-transition-nameを変更前後の要素に付与する必要があります。この時、変更前後で同じview-transition-nameが付与された要素がどちらも表示状態だとview-transition-nameの競合エラーが起きてしまうため、変更の前後で同じview-transition-nameを持つ要素が重複して表示されないようにCSS等で明確に非表示にしたり要素を削除する必要があります。

こちらのギャラリーページDEMOではモーダルを開くタイミングで変更前後の要素に同じview-transition-nameを付与することで要素ごとのトランジションを実装しています。<

Cross-document(MPA向け)の実装

MPAでのページ間トランジションは、なんとCSSを数行書くだけで有効になります。JavaScriptは不要です。

CSS

/* 遷移元・遷移先ページ両方のCSSに追記するだけ */
@view-transition {
  navigation: auto;
}

これだけでChrome・EdgeとSafariではページ遷移時にクロスフェードが付きます。view-transition-name::view-transition-old / new といったカスタマイズもSPAと同様に使えます。

Firefoxは現時点では未対応ですが、非対応ブラウザでは普通の遷移にフォールバックするだけなので、デプロイに踏み切ること自体は問題ありません。

エラー発生や読み込み長い場合などイレギュラー時の挙動について

エラー時にはブラウザがフリーズするのではなく、通常の遷移が行われます。さらに遷移後の状態の読み込みに時間がかかってしまう場合もMPAではエラーとなり通常の遷移になります。仕様に明記されていないもののChromeの開発ツールのネットワーク回線のスロットリングでテストしたところ1秒前後でタイムアウトしてしまっているように見えました。そのためFirefoxは想定しないケースでも遷移後のページの容量が重かったりしてレスポンスが遅いケースが想定される場合であればタイムアウトをより細かく制御できるswup.jsなどの導入を検討してみてください。

一方でSPAで時間がかかってしまうケースはView Transition APIを使わない場合でも同様に変更後の状態をfetch等で取得できるまで待機する処理が入るかと思いますが、ここでは自動的にタイムアウトがかからない可能性がありますので、このケースを想定する場合はあらかじめ変更後状態の取得にタイムアウトの処理を組み込む方が安全かと思われます。

swup.js との比較

以前の記事でご紹介したswup.jsとの使い勝手の違いをまとめました。

View Transition API swup.js
導入コスト ほぼゼロ(CSSのみでも可) npm install + 初期設定が必要
Firefoxの対応(MPA) ⚠️ 未対応(SPAはOK) ✅ 対応済み
カスタマイズ性 CSSで自由に制御 プラグインが豊富
ライフサイクルフック Promise形式で制御可 before / after などイベントが豊富
スクロール位置のリセット ブラウザが自動でやってくれる 設定が必要な場合あり
フォールバック 非対応ブラウザは普通に遷移 ライブラリがポリフィル的に動く
バンドルサイズ 0 約20KB(プラグインで増える)

ネイティブAPIで十分なケース

  • SPAでDOMの更新にアニメーションをつけたい
  • CMSベースのMPAで、ChromeとSafariへの対応で十分な場合
  • バンドルサイズを増やしたくない新規プロジェクト

swup.jsを引き続き選ぶケース

  • Firefoxも含めた均一な体験を担保したい
  • ページ遷移前後の処理(分析イベントの送信、スクリプトの再初期化など)を細かく制御したい
  • swup.jsのプラグインエコシステムをすでに活用している

新規プロジェクトであればView Transition APIをまず試してみて、要件が合わなければswup.jsを検討する、という順序が自然になってきていると思います。既存プロジェクトでswup.jsをすでに使っている場合は、無理に乗り換える必要はありません。

アクセシビリティへの配慮

「動きが多すぎて酔う」「アニメーションを止めたい」というユーザーへの配慮もあるとなお良いでしょう。prefers-reduced-motion へのケアは必ず入れるようにしましょう。

OSのアクセシビリティ設定「視差効果を減らす」をオンにしているユーザーには、アニメーションが無効になります。カスタムアニメーションを実装した場合は特に、この1ブロックを忘れないようにしてください。

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}

OSのアクセシビリティ設定「視差効果を減らす」をオンにしているユーザーには、アニメーションが無効になります。カスタムアニメーションを実装した場合は特に、この1ブロックを忘れないようにしてください。

まとめ

「便利そうだとは思っていたけど、難しそうで手が出なかった」という方も、まずはもっとも簡単な startViewTransition を試してみてください。最もシンプルなクロスフェードなら簡単さに驚くかと思います。

ディレクターの方も、「ページ遷移のアニメーション、入れられませんか?」という提案が以前よりずっと現実的なコストで実現できるようになっていますので、ぜひ選択肢に入れてみてくださいね!

Written by

山本 慎二 Shinji Yamamoto

front-end engineer

1987年に東京で生まれたが、1歳からは北海道で育つ。
2010年に札幌市立大学デザイン学部製品デザインコースを卒業後、イベント運営を行う会社に勤務し業務の一環でHTML/CSSに触れる。
2016年にcommono立ち上げ時から参加し、フロントエンドエンジニアとして学びながら現在に至る。

Q1. commonoに参加した理由
矢野さんが立ち上げメンバーを探しているときに、知人の紹介でお会いし、ここでなら前向きにワクワクできるようなことができると感じて参加させていただきました。
Q2. わたしの偏愛
マイホーム(今年こそピザ窯を)
Q3. 今後commonoというフィールドでやりたいこと
クライアント様やデザイナーと一緒にユーザーの心に残るような体験をWEBで表現する手伝いをしていきたいです。

Recommend

お問い合わせ

    個人情報利用目的への同意

    ※このサイトはCloudflare Turnstileよって保護されています。
    ※お客様が入力した個人情報はSSL暗号化信号によって保護されています。

    COMMONO株式会社
    E-mail:info@commono.co.jp
    個人情報保護方針、個人情報の取扱いについて
    PRIVACY POLICY

    お気に入り

    お気に入りから削除

    選択したアイテムをお気に入りから削除しますか?