Behind the Scenes: Dive into View Transition

Browser & UI #1 CSS
saku🌸 / @sakupi01

saku

🤗

Web Frontend Engineer
@Cybozu

🍏 / ☕ / 🌏 > ❤

Behind the Scenes: Dive into View Transition

Same-Document

View Transition

for SPA!

Mozilla is positive!
Selected to Interop 2025 Focus Area!

const { matches:motionIsOk } = window.matchMedia('(prefers-reduced-motion: no-preference)')

document.addEventListener("click", (e) => {
  if (!document.startViewTransition || !motionIsOk) {
    helloWorld.next();
    return;
  }
  document.startViewTransition(() => {
    helloWorld.next();
  });
});

どういうしくみ?????????

🤔

どういうしくみ?????????

🤔

Algorithms | CSS View Transition Module Level 1

UAデフォルトのView Transition (mac 54.56fps)

デフォルトの見た目

イベント:click / ブラウザ:スクショ撮る

デフォルトの見た目
document.addEventListener("click", (e) => {
  document.startViewTransition(() => {
  //   helloWorld.next();
  // });
});

イベント:DOMの変更完了 / ブラウザ:LIVEスクショ撮る

デフォルトの見た目
// document.addEventListener("click", (e) => {
  document.startViewTransition(() => {
    helloWorld.next();
  });
});

::view-transition-old()  / ::view-transition-new()

デフォルトの見た目 デフォルトの見た目
document.addEventListener("click", (e) => {
  document.startViewTransition(() => {
    helloWorld.next();
  });
});

Setup transition pseudo-elements

:root::view-transition  // Rendered on top of the document (over Top Layer)
└─ ::view-transition-group(root) // Position + Size
    └─ ::view-transition-image-pair(root) // Blend Mode Isolation
        ├─ ::view-transition-old(root) // 📸 Old Snapshot
        └─ ::view-transition-new(root) // 📸 New Live Snapshot
  • ::view-transition: documentをoriginating elementとして持つ擬似要素。(snapshot containing blockの領域を指す)
  • view-transition-group(root): view-transition-nameが同じ要素をグループ化する擬似要素
  • ::view-transition-image-pair(root): 新旧のスクリーンショットのペア。VTのUAデフォルトBlend Mode を正しく適用するため、isolation: isolateが指定されている。
  • ::view-transition-old(root): DOM変更前のスクリーンショット。デフォルトではmix-blend-mode: plus-lighterが指定されている。
  • ::view-transition-new(root): DOM変更後のスクリーンショット。デフォルトではmix-blend-mode: plus-lighterが指定されている。

Set up Default Animation

/* If capturedElement’s old image is null, then: */
:root::view-transition-new(transitionName) {
  animation-name: -ua-view-transition-fade-in;
}
/* If capturedElement’s new element is null, then: */
:root::view-transition-old(transitionName) {
  animation-name: -ua-view-transition-fade-out;
}
/* If both of capturedElement’s old image and new element are not null, then: */
@keyframes -ua-view-transition-group-anim-transitionName {
  from {
    transform: transform;
    width: width;
    height: height;
  }
}
:root::view-transition-group(transitionName) {
  animation-name: -ua-view-transition-group-anim-transitionName;
}
...
/* ref: https://www.w3.org/TR/css-view-transitions-1/#setup-transition-pseudo-elements-algorithm */

::view-transitionツリーがクリーンアップされる

〜完〜

アニメーションのカスタマイズ

  • view-transition-name
  • view-transition-class
  • view-transition-types

Cross-Document

View Transition

for MPA!

Cross-Document View Transition

  • View Transition without JavaScript!
/* ✅ Opt-in to cross-document view transitions! */
@view-transition {
  navigation: auto;
}

Cross-Document View Transition

  • View Transition without JavaScript!
/* ✅ Opt-in to cross-document view transitions! */
@view-transition {
  navigation: auto;
}
  • Available in Main frame or Nested iframe

Cross-Document View Transition

  • View Transition without JavaScript!
/* ✅ Opt-in to cross-document view transitions! */
@view-transition {
  navigation: auto;
}
  • Available in Main frame or Nested iframe
  • Same-Origin Navigation

Regulation of Cross-Document View Transition

  • ① WebKitベースブラウザをタッチデバイスで使った時のVTがダブる/できない
  • ② Cross-Document VTは宣言的なので、DOMの変更を明示できない
    • Same-Document VTでは、document.startViewTransitionのコールバックでDOMの変更を命令的に示すことができた

Regulation of Cross-Document View Transition

  • ① WebKitベースブラウザをタッチデバイスで使った時のVTがダブる/できない
  • ② Cross-Document VTは宣言的なので、DOMの変更を明示できない
    • Same-Document VTでは、document.startViewTransitionのコールバックでDOMの変更を命令的に示すことができた

WebKitベースブラウザをタッチデバイスで使った時のNavigation

  • ジェスチャーでナビゲーションすると、デフォルトで、ブラウザが前ページのプレビューを表示する
  • ブラウザのアニメーションとVTのアニメーションがダブる...

hasUAVisualTransition Property

  • ブラウザ:「こっちでアニメーションしといたから、もうやらなくていいよ」
  • アニメーションのダブりは防げるんだけど、そうじゃない...
const { matches:motionIsOk } = window.matchMedia('(prefers-reduced-motion: no-preference)')

document.addEventListener("click", (e) => {
  if (!document.startViewTransition || !motionIsOk || e.hasUAVisualTransition) {
    helloWorld.next();
    return;
  }
  document.startViewTransition(() => {
    helloWorld.next();
  });
});

Regulation of Cross-Document View Transition

  • ① WebKitベースブラウザをタッチデバイスで使った時のVT
  • ② Cross-Document VTは宣言的なので、DOMの変更を明示できない
    • Same-Document VTでは、document.startViewTransitionのコールバックでDOMの変更を命令的に示すことができた

Regulation of Cross-Document View Transition

  • ① WebKitベースブラウザをタッチデバイスで使った時のVT
  • ② Cross-Document VTは宣言的なので、DOMの変更を明示できない
    • Same-Document VTでは、document.startViewTransitionのコールバックでDOMの変更を命令的に示すことができた
    • CDVTはrender-blocking mechanismを利用してView Transitionを行なっている

ブラウザは基本的にインクリメンタルなレンダリングをする

  • render-blocking mechanism により、以下(など)の場合にレンダーがブロックされている
    • bodyがnullのとき
    • ブラウザエンジンのtimeoutを超えていないとき etc
  • +scriptとstylesheetでblock=renderingを指定して制御もできる

Document Render-Blocking

  • VTでは、「DOMの状態」が作成されるTransitionに影響を与える
  • → render-blockingをNodeによって制御できるといい
  • Nodeをrender-blockingの対象に指定できるように
< !-- Wait until the main-article element is seen and fully parsed before activating the transition -->
<link rel="expect" href="#main-article" blocking="render">

ref: https://www.w3.org/TR/css-view-transitions-2/#example-ece69f12

Intent to Ship: Document Render-Blocking

Speculation Rules API

  • 投機的にページをpre-renderingする
  • CDVTと併用するとGood
  • Chromiumのみ
<script type="speculationrules">
{
  "prerender": [
    {
      "urls": ["next.html", "next2.html"]
    }
  ]
}
</script>

⬆️ Upcoming View Transition Features ?!

Same-Site Cross-Origin View Transition

https://a.thing.com/ -> https://b.thing.com/

Same-origin (for now)
Cross-document view transitions are only enabled for same-origin navigations without a cross-origin redirect. In the future we could examine relaxing this restriction in some way to same-site navigations.
view-transitions/cross-doc-explainer.md at main · WICG/view-transitions

プライバシーの懸念があるので、慎重に検討されている


view-transition-name: match-element;, view-transition-name: auto;

  • view-transition-nameを自動生成してほしい!
  • view-transition-name: match-element;, view-transition-name: auto; の提案
    • view-transition-name: match-element;: DOM id 属性に関係なく、自動で「Element を参照した Identity」を生成する
    • view-transition-name: auto;: attr(id ident, match-element) のシンタックスシュガーみたいなもの
  • view-transition-name: auto;に関しては反対意見もある中、Safari 18.2で先立って実装された

Nested View Transition

  • DOMの構造がどれだけ複雑でも、::view-transitionツリーはフラットに生成される
  • clippingやtransform、animationなど、DOM構造がスタイルに影響する場合に対する提案
  <section class="container">
    <img class="icon">
  </section>
...
  <style>
    .container {
      clip-path: circle();
      view-transition-name: container;
    }
    .icon {
      view-transition-name: icon;
    }
  </style>
// container does not clip icon
::view-transition
  ::view-transition-group(container)
    ::view-transition-image-pair(container)
  ::view-transition-group(icon)
    ::view-transition-image-pair(icon)

Nested View Transition

  • DOMの構造がどれだけ複雑でも、::view-transitionツリーはフラットに生成される
  • clippingやtransform、animationなど、DOM構造がスタイルに影響する場合に対する提案
  <section class="container">
    <img class="icon">
  </section>
...
  <style>
    ::view-transition-group(container) {
        clip-path: circle();
    }
    .icon {
      view-transition-group: container; // or `nearest`
    }
  </style>
// container clips icon
::view-transition
  ::view-transition-group(container)
    ::view-transition-image-pair(container)
    ::view-transition-group(icon)
      ::view-transition-image-pair(icon)

Scoped View Transition

  • document直下にのみ::view-transitionは生成されていたが、任意のHTML要素に対して::view-transitionを生成できるようにする提案
    • VTの範囲を限定できる
  • VTの間、ページ全体がinertにならない(inertになる範囲を限定できる)
  • 親要素のclipやtransform, animationを考慮してVTが実行できる
  • z-indexを考慮してVTが実行できるので、ドロップダウンなどのVT領域をオーバーレイするUIに影響を与えない
  • 複数のVTを並行して実行できる
element.startViewTransition(() => {
  // Update the DOM somehow.
});

Reactがunstable_ViewTransitionを実験的に提供

Add <ViewTransition> Component by sebmarkbage · Pull Request #31975 · facebook/react

unstable_ViewTransition

まとめ

着実に進化を続けるView Transitionの今後に期待!

※ スポンサートークですが、弊社はView Transitionを使用しておりません

ご清聴ありがとうございました!

Thank you!

Appendix

When .startViewTransition() is called, the API captures the current state of the page. This includes taking a snapshot.

Once complete, the callback passed to .startViewTransition() is called. That's where the DOM is changed. Then, the API captures the new state of the page.

- すべてのアニメーションはCSSアニメーションで行われるため、CSSでカスタマイズ可能。

ネットワークめっちゃ遅延させた時、画像が読み込まれながらVTするので、LIVEスクショということもわかる