メインコンテンツまでスキップ
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

ビュートランジション

問題

ページの状態間やナビゲーション時にスムーズなアニメーショントランジションを作成するには、従来、複雑なJavaScriptアニメーションライブラリ、手動のDOM操作、またはReact Transition Groupのようなフレームワーク固有のソリューションが必要でした。ページナビゲーション(SPAとMPAの両方)では、視覚的な連続性のない唐突なコンテンツの切り替えが発生します。AIエージェントはJavaScriptに依存するアニメーションアプローチをデフォルトとし、View Transitions APIを提案することはほとんどありません。

解決方法

View Transitions APIは、DOM状態間のアニメーショントランジションを作成するためのネイティブメカニズムを提供します。ブラウザが古い状態のスナップショットをキャプチャし、DOM更新を適用してから、CSSを使って古いスナップショットと新しいスナップショットの間をアニメーションします。同一ドキュメント(SPA)のトランジションにはdocument.startViewTransition()を使用します。クロスドキュメント(MPA)のトランジションには@view-transition CSSアットルールを使って両方のページをオプトインします。

コード例

同一ドキュメントのView Transition(SPA)

/* Default crossfade animation — works with no extra CSS */
::view-transition-old(root) {
animation: fade-out 0.3s ease-out;
}

::view-transition-new(root) {
animation: fade-in 0.3s ease-in;
}

@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}

@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
<script>
function updateContent(newHTML) {
if (!document.startViewTransition) {
// Fallback: just update directly
document.getElementById('content').innerHTML = newHTML;
return;
}

document.startViewTransition(() => {
document.getElementById('content').innerHTML = newHTML;
});
}
</script>

要素レベルのアニメーション用の名前付きView Transition

view-transition-nameを割り当てることで、特定の要素に独自のトランジションを持たせます。

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

.product-title {
view-transition-name: product-title;
}

/* Customize the animation for the product image */
::view-transition-old(product-image) {
animation: scale-down 0.4s ease-in;
}

::view-transition-new(product-image) {
animation: scale-up 0.4s ease-out;
}

@keyframes scale-down {
from { transform: scale(1); }
to { transform: scale(0.8); opacity: 0; }
}

@keyframes scale-up {
from { transform: scale(0.8); opacity: 0; }
to { transform: scale(1); }
}

クロスドキュメントのView Transition(MPA)

@view-transitionアットルールで両方のページをトランジションにオプトインします。

/* Include this in BOTH the source and destination pages */
@view-transition {
navigation: auto;
}

/* Shared element transitions across pages */
.hero-image {
view-transition-name: hero;
}

/* Customize the cross-document transition */
::view-transition-old(hero) {
animation-duration: 0.4s;
}

::view-transition-new(hero) {
animation-duration: 0.4s;
}

ページ間のスライドトランジション

@view-transition {
navigation: auto;
}

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

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

::view-transition-old(root) {
animation: slide-to-left 0.4s ease-in-out;
}

::view-transition-new(root) {
animation: slide-from-right 0.4s ease-in-out;
}

view-transition-classによるグループアニメーション

CSSを繰り返すことなく、複数の名前付きトランジションに同じアニメーションを適用します。

.card-1 { view-transition-name: card-1; }
.card-2 { view-transition-name: card-2; }
.card-3 { view-transition-name: card-3; }

/* Apply the same animation class to all cards */
.card-1, .card-2, .card-3 {
view-transition-class: card;
}

/* One rule animates all card transitions */
::view-transition-group(*.card) {
animation-duration: 0.35s;
animation-timing-function: ease-in-out;
}

View Transition Typesによる条件付きトランジション

<script>
function navigateForward(updateFn) {
const transition = document.startViewTransition({
update: updateFn,
types: ['slide-forward'],
});
}

function navigateBack(updateFn) {
const transition = document.startViewTransition({
update: updateFn,
types: ['slide-back'],
});
}
</script>
/* Forward navigation */
:active-view-transition-type(slide-forward) {
&::view-transition-old(root) {
animation: slide-to-left 0.3s ease-in-out;
}
&::view-transition-new(root) {
animation: slide-from-right 0.3s ease-in-out;
}
}

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

ユーザー設定の尊重

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

ブラウザサポート

同一ドキュメントのView Transitions

  • Chrome 111+
  • Edge 111+
  • Safari 18+
  • Firefox 144+(2025年10月リリース — Interop 2025の一部)

クロスドキュメントのView Transitions

  • Chrome 126+
  • Edge 126+
  • Safari 18.2+
  • Firefox:未サポート

同一ドキュメントのトランジションは幅広いサポートがあります。クロスドキュメントのトランジションはChromiumとSafariでサポートされていますが、Firefoxではまだサポートされていません。常にdocument.startViewTransitionのサポートを確認してから呼び出すようにフォールバックを提供しましょう。

AIがよくやるミス

  • View Transitions APIがネイティブに処理できるトランジションに対して、JavaScriptアニメーションライブラリ(GSAP、Framer Motion)を使用する
  • document.startViewTransitionのサポートを呼び出し前に確認しない
  • クロスドキュメントトランジションで両方のページに@view-transition { navigation: auto; }を追加し忘れる
  • ページとは独立してアニメーションすべき共有要素にview-transition-nameを割り当てない
  • 同じページ上でview-transition-nameの値をユニークにしない(トランジション時に各名前はユニークでなければならない)
  • prefers-reduced-motionを尊重しない — モーションの軽減を好むユーザーのためにアニメーションを無効化または短縮する
  • 過剰なアニメーション:意味のある状態変化ではなく、すべての小さなUI更新にView Transitionsを使用する

使い分け

  • ページ間のナビゲーショントランジション(SPAとMPAの両方)
  • ページ内のコンテンツ更新(タブ切り替え、リストフィルタリング、詳細ビュー)
  • リストビューと詳細ビュー間の共有要素トランジション(例:商品サムネイル)
  • 視覚的な連続性がユーザーの変化の理解を助けるあらゆる状態変化
  • 複雑なJavaScriptアニメーションの設定をネイティブブラウザ機能に置き換える

ライブプレビュー

View Transition CSSパターン(静的例)

参考リンク