🎥

Svelte でView-TransitionsAPI を使用する

2024/12/09に公開

View Transitions APIの紹介

こちらLabbaseのOscarです。最近、view-transitions APIについて読んでいて、ブラウザのサポートがどんどん良くなってきているので、以下のようなエフェクトの作り方を学んでみるのはどうでしょうか(2024年12月現在 - すでにChromeで動作するはずです!)。

基本概念

View Transitions APIは、ウェブサイトの異なるビュー間でアニメーションによる遷移を簡単に作成するための仕組みを提供します。これには、シングルページアプリケーション(SPA)におけるDOM状態間のアニメーションや、マルチページアプリケーション(MPA)におけるページ間のナビゲーションのアニメーションが含まれます。

前述の例をコードを見ずに見ると、このエフェクトはJavaScriptによるスタイルの変更や、CSSクラスの追加によって作られていると思うかもしれません。しかし、この場合は実際に2つのDOM状態間の遷移を直接アニメーション化しているのです。これがview-transitions APIで可能になることです。1つのページの2つのDOM状態間、あるいは両方のページが適切に設定されていれば、あるページから別のページへの遷移もアニメーション化できます。さらに、作業の大部分はCSSで行われるため、リソースとコードの負担が軽くなります。ブラウザのサポートが完全に整えば、異なるブラウザ間で同じコードで予測可能にアニメーションを実行できるという一貫性の利点も得られます。

実装方法

必要なコードのスニペットは以下のようにシンプルです。

document.startViewTransition(() => { <DOM manipulation>});

DOMを操作する任意のJavaScriptコードをdocument.startViewTransition()でラップすることで、view-transitions APIは以下の手順を実行します:

  • ブラウザの状態の「スクリーンショット」を撮影
  • document.startViewTransition()に渡されたコールバック内のすべてのコードを実行
  • 新しい「スクリーンショット」を撮影
  • CSSルールに従って2つの状態間の遷移をアニメーション化

Svelteでの実装例

冒頭のSvelteの例では、基本的なコードは以下のようにシンプルです:

<script>
  import Card from "./Card.svelte";
  import ExpandedCard from "./ExpandedCard.svelte";


  let expanded = false;
  const handleClick = () => {
    document.startViewTransition(() => { expanded = !expanded; });
  };
</script>

<div>
  {#if expanded}
    <ExpandedCard {handleClick}/>
  {:else}
    <Card {handleClick}/>    
  {/if}
</div>

上記のSvelteコードでは、クリックハンドラーがexpandedという二値の状態変数の切り替えを行います。これにより、"Card"または"ExpandedCard"コンポーネントが状態に基づいて選択的にレンダリングされます。

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

この簡単な例では、デフォルトのトランジション(シンプルなクロスフェード)は実際にはかなり適していますが(サンプルコードからトランジションCSSを削除して試してみてください)、適切な使用方法を示すために、スムーズなスライドアウトを作成しました。

このアニメーションのCSSは以下の通りです:

:global(::view-transition-old(card)),
:global(::view-transition-new(card)) {
    animation-duration: 0.5s;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    width: auto;
    height: auto;
}

:global(::view-transition-old(card)) {
    animation-name: slide-exit;
}

:global(::view-transition-new(card)) {
    animation-name: slide-enter;
}

@keyframes slide-exit {
from {
    transform: translateX(0) scale(1);
    opacity: 1;
}
to {
    transform: translateX(300%);
    opacity: 0;
}
}

@keyframes slide-enter {
from {
    transform: translateX(-300%);
    opacity: 0;
}
to {
    transform: translateX(0) scale(1);
    opacity: 1;
}
}

CSSの詳細な解説

上記の例では、slide-enterslide-exitは基本的にシーンへの出入り時のアニメーションの実行方法を定義しています。

:global(::view-transition-old(card)) {
  animation-name: slide-exit;
}
:global(::view-transition-new(card)) {
  animation-name: slide-enter;
}

このコードは、以下のCSSプロパティを持つ要素の出入り時のアニメーションを割り当てています:

view-transition-name: card;

そして最後に、これがアニメーションの両方のパートに適用されます:

:global(::view-transition-old(card)),
:global(::view-transition-new(card)) {
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  width: auto;
  height: auto;
}

順番に説明すると:

  • アニメーションの持続時間を一定に設定します
  • スムージングを伴う心地よいモーションをアニメーションに選択します
  • アニメーション中に要素が縮小または拡大しないよう(この場合は望ましくないため)、一貫した幅と高さを確保します

これらの基本要素を使って、完全なアニメーションをもう一度見てみましょう!

おわりに

以上、LabbaseのOscarがお送りしました。ご覧いただきありがとうございます!

Discussion