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}
display
切替時
CSSの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>
visibility
切替時
CSSの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>
display
切替時
CSSのシーケンシャルな切替
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>
visibility
切替時
CSSの
移動アニメーション
親要素が変わらない移動
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