🍫

Svelteのtransition機能を使ってみる

2023/11/26に公開

はじめに

個人開発でSvelte, SvelteKitを利用しています。
svelteにはtransitionという機能が用意されています。この機能は触ってみると結構便利な点がありました。
今回はこれを使ってsnackbarを実装してみます。

前提

"svelte": "4.2.7",
"@sveltejs/kit": "1.27.6",
"@pandacss/dev": "0.19.0",

svelte/transitionとは?

https://svelte.jp/docs/svelte-transition
自前でcssを使わなくてもdom要素にアニメーションを追加できる機能(関数)です。
svelteには現在7つのtransition関数が用意されています。
今回はsnackbarの実装をしたいので、slideを使うことにしました。

  • fade
  • blur
  • fly
  • slide
  • scale
  • draw
  • crossfade

以下がざっくりとした実装例です。
多少のstyleは実装する必要がありますが、これだけでslideのアニメーションを実装することができるのは手軽で良いですね。

Snackbar.svelte
<script lang="ts">
  import { slide } from 'svelte/transition'

  // buttonのclickイベント等で値の更新をする
  let visible: boolean = false
</script>

{#if visible}
  <div class="slide" transition:slide={{ delay: 200, duration: 300, axis: 'x' }} >
    slide transition
  </div>
{/if}

<style>
  .slide {
    position: fixed;
    width: 200px;
    bottom: 20px;
    right: 20px;
    background-color: white;
  }
</style>

この例ではslideにdelay, duration, axisといったパラメータを設定していています。それぞれ以下を意味しています。

  • delay: アニメーション開始をどのくらい遅らせるか(ミリ秒)
  • duration: animationの開始から終了までの時間(ミリ秒)
  • axis: slideの動きの方向(xまたはy)

各transitionでは、このようにアニメーションに変化を与えるためのパラメータを設定可能です(各transition関数によって渡せるパラメータが一部異なる)。

今回の実装では使いませんが、transition-eventsというものも便利な機能です。
introstart, introend, outrostart, outroend といったように、アニメーションのタイミングに合わせて処理を割り当てることができます。
https://svelte.jp/docs/element-directives#transition-events

今回の実装方針

ざっと今回の実装方針は以下のよう感じです。

  • Snackbarコンポーネントは、routes直下の直下の+layout.svelteで呼び出す
  • 上記のため、snackbarに関する状態管理はグローバルな状態として管理する(storeを利用)

実際の実装

状態管理

前述した通り、svelteのstore機能を使って状態管理します。

https://github.com/YutaroMatsumoto/shellf/blob/main/src/globalStates/snackbar.ts#L1-L29

+layout.svelte(必要な部分だけ)

+layout.svelte
<script lang="ts">
	// ...省略
	import { createSnackbar } from '$globalStates/snackbar'
	import Snackbar from '$ui/Snackbar/Snackbar.svelte'
	const snackbars = createSnackbar
</script>

<!-- ...省略 -->
<div class={snackbarContainer}>
	{#each $snackbars as snackbar (snackbar.id)}
		<Snackbar status={snackbar.status} message={snackbar.message} />
	{/each}
</div>

https://github.com/YutaroMatsumoto/shellf/blob/main/src/components/ui/_layout/Layout.style.ts#L15-L21

Snackbarコンポーネント

ここで1つ注意点があります。
globalで状態を管理しているからといって、Layoutとは別でsnackbarのstoreを呼び出してその値をコンポーネントで使うことは避けた方が良いです。
理由は以下の恩恵を受けられなくなるためです。

おそらくですがtransitionにはアニメーションの完了までdom側で状態を保持してくれる機能があります。
今回の実装だと、addSnackbarの処理では、指定した時間後に対象のsnackbarの状態を削除するようになっています。
本来であればsnackbarはoutrostartのタイミングでmessage等の状態は消えているはずですが、dom側ではoutroendのタイミングまでmessageやstatusが確認できているのがその証拠です。

https://github.com/YutaroMatsumoto/shellf/blob/main/src/components/ui/Snackbar/Snackbar.svelte#L1-L37

今回は以上です。

Discussion