View Transitions APIで一覧切り替えをなめらかにする実装

はじめに

一覧の表示切り替えを実装するとき、DOM の更新自体は簡単でも、切り替わりが唐突だと操作感が少し硬く見えます。JavaScript でクラスを付け替えて細かくアニメーションを管理する方法もありますが、画面状態が増えるほど調整ポイントが増えがちです。

そんな場面で扱いやすいのが View Transitions API です。document.startViewTransition() で DOM 更新を包むだけで、同一ドキュメント内の状態変化を自然につなげやすくなります。

この記事では、フィルター付きの商品グリッドを題材に、基本の導入から実務向けの応用までをまとめます。

機能やライブラリの概要

View Transitions API は、同一ドキュメント内の UI 切り替えを滑らかに見せるための Web API です。MDN では Document.startViewTransition() を Baseline 2025 の機能として案内しており、最新版ブラウザーでは使いやすさがかなり上がっています。

ポイントは次の3つです。

  • DOM の更新処理をそのまま活かしながら、切り替え演出を追加できる
  • view-transition-name を使うと、特定要素だけ別アニメーションにできる
  • 非対応環境では通常の DOM 更新にフォールバックしやすい

従来の「状態切り替えごとに入れ替え用クラスを増やす」実装より、表示更新ロジックとアニメーションの責務を分けやすいのが実務上の利点です。

インストール方法

View Transitions API はブラウザー標準機能なので、ライブラリの追加は不要です。まずは対応ブラウザーを確認したうえで、既存の JavaScript に段階的に組み込むのが安全です。

準備として必要なのは、次の2点です。

  • document.startViewTransition の有無を確認してフォールバックを書く
  • 別スナップショットで動かしたい要素に view-transition-name を付ける

基本の使い方

まずは一覧の表示条件を切り替える最小構成です。ボタン押下時に表示対象を変えるだけでも、切り替わりがかなり見やすくなります。

HTML

HTML
<div class="filter-bar">
  <button type="button" data-filter="all">すべて</button>
  <button type="button" data-filter="design">デザイン</button>
  <button type="button" data-filter="code">実装</button>
</div>

<section class="item-grid" id="js-item-grid">
  <article class="item-card" data-category="design">カード A</article>
  <article class="item-card" data-category="code">カード B</article>
  <article class="item-card" data-category="design">カード C</article>
</section>

JS

JavaScript
const buttons = document.querySelectorAll("[data-filter]")
const cards = document.querySelectorAll(".item-card")

function updateGrid(filter) {
  cards.forEach((card) => {
    const visible = filter === "all" || card.dataset.category === filter
    card.hidden = !visible
  })
}

buttons.forEach((button) => {
  button.addEventListener("click", () => {
    const filter = button.dataset.filter

    if (!document.startViewTransition) {
      updateGrid(filter)
      return
    }

    document.startViewTransition(() => {
      updateGrid(filter)
    })
  })
})

この書き方なら、既存の描画更新関数を大きく崩さずに導入できます。まず通常更新を書き、その外側だけ View Transition 対応にする流れが分かりやすいです。

便利な使いどころ

View Transitions API が特に効くのは、次のような UI です。

  • 絞り込み付き一覧の表示切り替え
  • タブ切り替えとパネル更新
  • モーダル内のステップ遷移
  • 一覧から詳細への同一ページ内切り替え

共通しているのは、「ページ遷移ではないが、状態変化を少し丁寧に見せたい」場面です。演出を派手にするというより、文脈を途切れさせないための補助として使うと相性が良いです。

応用コード

実務では、カード全体はクロスフェード、見出し画像だけは個別に移動させたいことがあります。そういうときは view-transition-name を使うと整理しやすいです。

HTML

HTML
<section class="feature-panel" id="js-feature-panel">
  <article class="feature-card is-active" data-view="overview">
    <img src="/images/overview.jpg" alt="" class="feature-card__image" />
    <h2>概要</h2>
    <p>一覧を確認するための説明です。</p>
  </article>

  <article class="feature-card" data-view="detail" hidden>
    <img src="/images/detail.jpg" alt="" class="feature-card__image" />
    <h2>詳細</h2>
    <p>選択した内容を深掘りするための説明です。</p>
  </article>
</section>

CSS

CSS
.feature-card__image {
  view-transition-name: panel-image;
}

::view-transition-old(panel-image) {
  animation: panel-image-out 180ms ease;
}

::view-transition-new(panel-image) {
  animation: panel-image-in 260ms ease;
}

@keyframes panel-image-out {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(0.96);
  }
}

@keyframes panel-image-in {
  from {
    opacity: 0;
    transform: scale(1.04);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

JS

JavaScript
const panel = document.querySelector("#js-feature-panel")
const viewButtons = document.querySelectorAll("[data-view-trigger]")

function switchPanel(nextView) {
  panel.querySelectorAll(".feature-card").forEach((card) => {
    const active = card.dataset.view === nextView
    card.hidden = !active
    card.classList.toggle("is-active", active)
  })
}

viewButtons.forEach((button) => {
  button.addEventListener("click", async () => {
    const nextView = button.dataset.viewTrigger

    if (!document.startViewTransition) {
      switchPanel(nextView)
      return
    }

    const transition = document.startViewTransition(() => {
      switchPanel(nextView)
    })

    await transition.finished
  })
})

この形にしておくと、状態更新は switchPanel() に閉じ込めたまま、演出だけ CSS 側で調整できます。JavaScript と CSS の役割分担が明確なので、改修時に崩れにくいです。

注意点

  • 非対応環境の分岐は必須です
    document.startViewTransition がない環境では通常更新へ落とす前提で組むと安全です。
  • view-transition-name は重複管理に注意します
    同じ遷移内で意図しない要素まで同名にすると、期待しないアニメーションになることがあります。
  • 大きな DOM 差し替えは演出より更新設計を先に見直します
    すべてを遷移で解決しようとすると、状態管理やアクセシビリティの整理が後回しになりやすいです。
  • 動きの量は控えめにします
    一覧 UI は操作頻度が高いので、長すぎる演出より短めの補助表現のほうが使いやすいです。

まとめ

View Transitions API は、同一ページ内の表示切り替えを JavaScript 主体で扱いやすくする API です。既存の DOM 更新処理を包むだけで導入を始めやすく、必要なら view-transition-name で要素単位の演出にも広げられます。

まずは絞り込み一覧やタブ切り替えのような、状態変化が明確な UI に入れると効果を判断しやすいです。小さく導入して、効く場面だけ残す進め方が実務では扱いやすいと思います。

ポイント

  • document.startViewTransition() で既存の DOM 更新処理を包むだけでも導入しやすい
  • view-transition-name を使うと特定要素だけ別演出にできる
  • 非対応環境へのフォールバックを先に書くと安全に展開しやすい

参考リンク

read next