Svelte5の各状況でのアニメーション実装方法メモ
Svelte5で色々作成している中で要素にアニメーションを付与したいと思った時、この状況だと何を使えばよかったっけ、となることがまぁまぁあるのでメモとして纏めました。記載以外のHTML/CSSネイティブ機能を使用する等、これ以外にも方法はあると思うので参考です。easing関数やtransitionの種類,引数については別の場所にリファレンスや記事等あるため記載せず参考文献にそれらへのリンクを載せています。
まとめ
| 分類 | トリガー | 指定方法 |
|---|---|---|
| 1要素表示 | 要素存在 |
transitionディレクティブ |
| 1要素表示 | CSS display
|
CSStransitionと@starting-style
|
| 1要素表示 | CSS visibility
|
CSStransition
|
| 1要素表示 | CSS 疑似クラス | CSStransition
|
| 2要素表示 | 要素存在 |
transitionディレクティブのdurationとdelayを同期 |
| 2要素表示 | CSS display,visibility
|
setTimeoutでtransition-durationと同期 |
| 移動 | 同親内の位置変更 | CSStransition
|
| 移動 | 別親内へ要素移動 |
in,outディレクティブとcrossfade関数 |
| 移動 | 同親内の子要素増減時 | Keyed each blockとanimateディレクティブとflip関数 |
1要素の表示切替アニメーション
作成,削除時
transitionディレクティブを使用する。
code
<script module lang="ts">
import { fade } from "svelte/transition";
</script>
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
{#if bool}
<p transition:fade>Hello</p>
{/if}
作成時と削除時で異なるアニメーションを設定する場合はin,outディレクティブを使用する。
code
<script module lang="ts">
import { slide, fade } from "svelte/transition";
</script>
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
{#if bool}
<p in:slide out:fade>Hello</p>
{/if}
CSSのdisplay切替時
CSSのtransitionプロパティ等を使用する。
code
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
<p data-display={bool}>Hello</p>
<style>
p {
transition-property: display, opacity;
transition-behavior: allow-discrete;
transition-duration: 400ms;
}
p[data-display="true"] {
display: block;
opacity: 1;
@starting-style { opacity: 0; }
}
p[data-display="false"] {
display: none;
opacity: 0;
}
</style>
CSSのvisibility切替時
CSSのtransitionプロパティを使用する。
code
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
<p data-display={bool}>Hello</p>
<style>
p {
transition-property: visibility, opacity;
transition-duration: 400ms;
}
p[data-display="true"] {
visibility: visible;
opacity: 1;
}
p[data-display="false"] {
visibility: hidden;
opacity: 0;
}
</style>
CSSの疑似要素トグル時
CSSのtransitionプロパティを使用する。
code
<p>Hello</p>
<style>
p {
font-size: 1rem;
transition-property: font-size;
transition-duration: 400ms;
}
p:hover {
font-size: 1.5rem;
}
</style>
2要素の表示切替アニメーション
作成,削除時
シーケンシャルな切替
transitionディレクティブのdurationとdelayを同期するよう設定する。
-
{#if}を使用する場合
code
<script module lang="ts">
import { fade } from "svelte/transition";
</script>
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
{#if bool}
<p in:fade={{duration: 400, delay: 400}} out:fade={{duration: 400}}>Good morning</p>
{:else}
<p in:fade={{duration: 400, delay: 400}} out:fade={{duration: 400}}>Good afternoon</p>
{/if}
-
{#key}を使用する場合
code
<script module lang="ts">
import { fade } from "svelte/transition";
</script>
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
{#key bool}
<p in:fade={{duration: 400, delay: 400}} out:fade={{duration: 400}}>
{bool ? "Good morning" : "Good afternoon"}
</p>
{/key}
パラレルな切替
CSSで位置を揃える。
code
<script module lang="ts">
import { fade } from "svelte/transition";
</script>
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
<div>
{#if bool}
<p transition:fade>Good morning</p>
{:else}
<p transition:fade>Good afternoon</p>
{/if}
</div>
<style>
div { position: relative; }
p {
position: absolute;
left: 0;
top: 0;
}
</style>
CSSのdisplay切替時
シーケンシャルな切替
setTimeoutでtransition-durationと切替タイミングを同期させる。
code
<script lang="ts">
let bool1 = $state(false);
let bool2 = $state(true);
function onclick() {
if (bool1) {
bool1 = !bool1;
setTimeout(() => bool2 = !bool2, 400);
} else {
bool2 = !bool2;
setTimeout(() => bool1 = !bool1, 400);
}
}
</script>
<button type="button" {onclick}>on/off</button>
<p data-display={bool1}>Good morning</p>
<p data-display={bool2}>Good afternoon</p>
<style>
p {
transition-property: display, opacity;
transition-behavior: allow-discrete;
transition-duration: 400ms;
}
p[data-display="true"] {
display: block;
opacity: 1;
@starting-style { opacity: 0; }
}
p[data-display="false"] {
display: none;
opacity: 0;
}
</style>
パラレルな切替
CSSで位置を揃える。
code
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
<div>
<p data-display={bool}>Good morning</p>
<p data-display={!bool}>Good afternoon</p>
</div>
<style>
div { position: relative; }
p {
position: absolute;
left: 0;
top: 0;
transition-property: display, opacity;
transition-behavior: allow-discrete;
transition-duration: 400ms;
}
p[data-display="true"] {
display: block;
opacity: 1;
@starting-style { opacity: 0; }
}
p[data-display="false"] {
display: none;
opacity: 0;
}
</style>
View Transition APIで切替
document.startViewTransitionで$state変数を変化させる。
code
<script lang="ts">
let page = $state("1");
function onclickF(p: string): ()=>void {
return () => {
document.startViewTransition(() => { page = p; });
};
}
</script>
<div style={`display: ${page === "1" ? "block" : "none"};`}>
<button type="button" onclick={onclickF("2")}>hello</button>
<button type="button" onclick={onclickF("3")}>world</button>
<hr>
<p class="hello">HELLO!!</p>
<p class="world">WORLD!!</p>
</div>
<div style={`display: ${page === "2" ? "block" : "none"};`}>
<button type="button" onclick={onclickF("1")}>back</button>
<p class="hello">HELLO!!</p>
</div>
<div style={`display: ${page === "3" ? "block" : "none"};`}>
<button type="button" onclick={onclickF("1")}>back</button>
<p class="world">WORLD!!</p>
</div>
<style>
.hello { view-transition-name: foo; }
.world { view-transition-name: bar; }
</style>
CSSのvisibility切替時
移動アニメーション
親要素が変わらない移動
CSSのtransitionプロパティを使用する。
code
<script lang="ts">
let bool = $state(false);
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
<div>
<p data-bool={bool}>hello</p>
</div>
<style>
div {
position: relative;
width: 400px;
height: 400px;
border: solid;
border-width: 1px;
border-color: blue;
}
p {
margin: 0px;
position: absolute;
width: 50px;
height: 50px;
border: solid;
border-width: 1px;
transition: all 400ms;
}
p[data-bool="false"] {
left: 10px;
top: 10px;
}
p[data-bool="true"] {
left: 340px;
top: 340px;
}
</style>
親要素が変わる移動
in,outディレクティブとcrossfadeを使用する。
code
<script module lang="ts">
import { crossfade } from "svelte/transition";
const [send, receive] = crossfade({});
</script>
<script lang="ts">
let bool = $state(true);
const key = "key";
function onclick() {
bool = !bool;
}
</script>
<button type="button" {onclick}>on/off</button>
<div>
{#if bool}
<p in:receive={key} out:send={key}></p>
{/if}
</div>
<div>
{#if !bool}
<p in:receive={key} out:send={key}></p>
{/if}
</div>
<style>
div {
padding: 10px;
border: solid;
border-width: 1px;
border-color: blue;
width: 400px;
height: 400px;
}
p {
margin: 0px;
border: solid;
border-width: 1px;
width: 50px;
height: 50px;
}
</style>
子要素増減による移動
{#each items as item (item.key)}を用いて子要素にanimate:flipディレクティブを使用する。
code
<script module lang="ts">
import { crossfade } from "svelte/transition";
import { flip } from "svelte/animate";
const [send, receive] = crossfade({});
</script>
<script lang="ts">
const keys1 = $state([1,2,3].map(x => { return {key: x}; }));
const keys2 = $state([4,5,6].map(x => { return {key: x}; }));
function onclick() {
keys2.push(keys1.shift());
setTimeout(() => keys1.push(keys2.shift()), 100);
}
</script>
<button type="button" {onclick}>on/off</button>
<ul>
{#each keys1 as {key} (key)}
<li in:receive={key} out:send={key} animate:flip={{duration: 400}}>{key}</li>
{/each}
</ul>
<ul>
{#each keys2 as {key} (key)}
<li in:receive={key} out:send={key} animate:flip={{duration: 400}}>{key}</li>
{/each}
</ul>
<style>
ul {
list-style-type: none;
display: flex;
overflow: hidden;
padding: 10px;
border: solid;
border-width: 1px;
border-color: blue;
width: 400px;
height: 400px;
}
li {
margin: 0px;
border: solid;
border-width: 1px;
width: 50px;
height: 50px;
}
</style>
アニメーション中の値を動的に取得
svelte/motionのTweenもしくはSpringを使用する。
雑記
SvelteのPlaygroundのデフォルトタイトル"Hello world"の左にあるメニューアイコンを開くと色々なサンプルが見れることに今更気が付きました。Recursive componentsなどできることが広がりそうなサンプルもあるので、また時間がある時に眺めたいと思いました。
Discussion