Svelte and SvelteKit course · Module 9: Transitions and Animations
Custom Transitions
In this lesson4
Create your own transitions:
Custom transition structure
import { cubicOut } from 'svelte/easing';
function myTransition(node, params) {
return {
delay: params.delay || 0,
duration: params.duration || 400,
easing: params.easing || cubicOut,
css: (t, u) => `
transform: scale(${t});
opacity: ${t};
`
// t: 0 -> 1 (in), 1 -> 0 (out)
// u: 1 - t
};
}Example: Spin transition
<script>
import { cubicOut } from 'svelte/easing';
function spin(node, { duration = 500, times = 1 }) {
return {
duration,
css: (t) => {
const degrees = times * 360 * t;
return `
transform: rotate(${degrees}deg) scale(${t});
opacity: ${t};
`;
}
};
}
let visible = $state(true);
</script>
{#if visible}
<div transition:spin={{ duration: 800, times: 2 }}>
Spinning!
</div>
{/if}Transition with tick (for JS animation)
function typewriter(node, { speed = 50 }) {
const text = node.textContent;
const duration = text.length * speed;
return {
duration,
tick: (t) => {
const i = Math.floor(text.length * t);
node.textContent = text.slice(0, i);
}
};
}draw - For SVG
<script>
import { draw } from 'svelte/transition';
let visible = $state(true);
</script>
<svg viewBox="0 0 100 100">
{#if visible}
<path
transition:draw={{ duration: 1000 }}
d="M10 10 L90 10 L90 90 L10 90 Z"
fill="none"
stroke="gold"
stroke-width="2"
/>
{/if}
</svg>Code for this lesson: App.svelte
<script>
// in: i out: - separate enter/exit animations
import { fly, fade, scale } from 'svelte/transition';
let items = $state([
{ id: 1, name: 'Ragnar' },
{ id: 2, name: 'Bjorn' },
{ id: 3, name: 'Ivar' }
]);
let nextId = 4;
function addItem() {
const names = ['Ubbe', 'Sigurd', 'Hvitserk', 'Lagertha', 'Rollo'];
const name = names[Math.floor(Math.random() * names.length)];
items = [...items, { id: nextId++, name }];
}
function removeItem(id) {
items = items.filter(i => i.id !== id);
}
</script>
<main class="in-out-demo">
<h1>in: vs out:</h1>
<p class="subtitle">Different animations for enter and exit</p>
<button onclick={addItem}>+ Add warrior</button>
<div class="list">
{#each items as item (item.id)}
<div
class="item"
in:fly={{ x: -200, duration: 400 }}
out:fade={{ duration: 200 }}
>
<span>{item.name}</span>
<button onclick={() => removeItem(item.id)}>Remove</button>
</div>
{/each}
</div>
<section class="code">
<h2>Syntax</h2>
<pre>
{"<div"}
in:fly={"{"}{"{"} x: -200 {"}"}{"}"}
out:fade
{">"}
Different animations!
{"</div>"}
</pre>
</section>
</main>
<style>
.in-out-demo {
font-family: 'Georgia', serif;
max-width: 400px;
margin: 0 auto;
padding: 2rem;
background: #1a0a0a;
color: #f7d5e8;
min-height: 100vh;
}
h1 {
color: #ff6b6b;
text-align: center;
}
.subtitle {
text-align: center;
color: #a87ca8;
margin-bottom: 1rem;
}
button {
padding: 0.5rem 1rem;
background: #3a1a2a;
color: #f7d5e8;
border: 1px solid #ff6b6b;
border-radius: 4px;
cursor: pointer;
}
.list {
margin-top: 1rem;
}
.item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem;
background: rgba(255, 107, 107, 0.1);
border: 1px solid #ff6b6b;
border-radius: 4px;
margin-bottom: 0.5rem;
}
.item button {
background: #8b0000;
border-color: #8b0000;
padding: 0.25rem 0.5rem;
}
.code {
margin-top: 2rem;
background: rgba(0,0,0,0.5);
border-radius: 8px;
padding: 1rem;
}
.code h2 {
color: #ff6b6b;
font-size: 1rem;
margin-top: 0;
}
.code pre {
font-size: 0.9rem;
margin: 0;
}
</style>Docs entries used in this lesson
Spotted a mistake in this lesson?