🎭 Svelte Advanced Transitions: The Magic Show
Imagine you’re a magician. You don’t just make things disappear—you make them dance, swap places, and tell the audience exactly when the trick starts and ends. That’s what advanced transitions in Svelte let you do!
🌟 The Big Picture
Svelte has built-in transitions like fade and slide. But what if you want something totally unique? What if you want two things to swap places smoothly? What if you need to know exactly when an animation starts or finishes?
That’s where Advanced Transitions come in:
| Power | What It Does |
|---|---|
| Custom Transitions | Build your own magic tricks |
| Crossfade | Make items swap places beautifully |
| Transition Events | Know when animations start/end |
🎨 Custom Transitions: Build Your Own Magic
What Is It?
Think of built-in transitions as toys from a store. Custom transitions are like building your own toy from scratch. You decide everything!
The Simple Recipe
A custom transition is just a function that returns an object with instructions:
<script>
function myMagic(node, params) {
return {
delay: 0,
duration: 400,
css: (t) => `opacity: ${t}`
};
}
</script>
<div transition:myMagic>
I appear with custom magic!
</div>
Breaking It Down
| Part | What It Means |
|---|---|
node |
The HTML element getting animated |
params |
Extra settings you can pass in |
delay |
Wait before starting (milliseconds) |
duration |
How long the animation takes |
css |
A function that returns CSS styles |
t |
Goes from 0 → 1 (entering) or 1 → 0 (leaving) |
🎯 Real Example: Spin & Grow
Let’s make an element spin while it appears:
<script>
function spinIn(node, { duration = 500 }) {
return {
duration,
css: (t) => `
transform: rotate(${t * 360}deg)
scale(${t});
opacity: ${t};
`
};
}
let visible = true;
</script>
<button on:click={() => visible = !visible}>
Toggle
</button>
{#if visible}
<div transition:spinIn={{ duration: 800 }}>
🌟 I spin into existence!
</div>
{/if}
🔧 Using the tick Function
For complex animations, you might use tick instead of css:
<script>
function typewriter(node, { speed = 50 }) {
const text = node.textContent;
const len = text.length;
return {
duration: len * speed,
tick: (t) => {
const i = Math.floor(len * t);
node.textContent = text.slice(0, i);
}
};
}
</script>
{#if show}
<p transition:typewriter>
Hello, I type letter by letter!
</p>
{/if}
Pro Tip: Use
csswhen possible—it’s faster! Usetickonly when you need JavaScript control.
🔄 Crossfade: The Swap Dance
What Is It?
Imagine you have a to-do list. When you move an item from “To Do” to “Done”, wouldn’t it be cool if it flew smoothly from one list to the other?
That’s crossfade! It makes elements look like they’re traveling between locations.
How It Works
graph TD A["Item in List A"] -->|Crossfade| B["Same Item in List B"] C["Position A"] -->|Smooth Animation| D["Position B"]
The Magic Setup
<script>
import { crossfade } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
const [send, receive] = crossfade({
duration: 400,
easing: quintOut
});
let todos = [
{ id: 1, text: 'Learn Svelte', done: false },
{ id: 2, text: 'Build app', done: false }
];
function toggle(id) {
todos = todos.map(t =>
t.id === id ? {...t, done: !t.done} : t
);
}
</script>
Using Send & Receive
<div class="lists">
<div class="todo">
<h2>To Do</h2>
{#each todos.filter(t => !t.done) as todo (todo.id)}
<div
in:receive={{ key: todo.id }}
out:send={{ key: todo.id }}
on:click={() => toggle(todo.id)}
>
{todo.text}
</div>
{/each}
</div>
<div class="done">
<h2>Done</h2>
{#each todos.filter(t => t.done) as todo (todo.id)}
<div
in:receive={{ key: todo.id }}
out:send={{ key: todo.id }}
on:click={() => toggle(todo.id)}
>
{todo.text}
</div>
{/each}
</div>
</div>
🔑 The Key Concept
The key links items together:
| Directive | When Used | What Happens |
|---|---|---|
out:send |
Item leaves | Remembers position |
in:receive |
Item arrives | Animates from old position |
Both use the same key so Svelte knows they’re the same item!
Crossfade Options
const [send, receive] = crossfade({
duration: 400, // Animation time
delay: 0, // Wait before starting
easing: quintOut, // How motion feels
fallback: fade // What to do if no match
});
Fallback Magic: If an item has no matching partner (like when first appearing), the
fallbacktransition plays instead!
📣 Transition Events: The Announcer
What Are They?
Sometimes you need to know:
- “Hey, the animation just started!”
- “The animation is now done!”
Transition events are like having an announcer that tells you exactly what’s happening.
The Four Events
graph LR A["introstart"] --> B["introend"] C["outrostart"] --> D["outroend"]
| Event | When It Fires |
|---|---|
introstart |
Element starts appearing |
introend |
Element finished appearing |
outrostart |
Element starts leaving |
outroend |
Element finished leaving |
How To Listen
<script>
import { fade } from 'svelte/transition';
let visible = true;
let status = 'Ready';
function handleIntroStart() {
status = '✨ Appearing...';
}
function handleIntroEnd() {
status = '🎉 Fully visible!';
}
function handleOutroStart() {
status = '👋 Leaving...';
}
function handleOutroEnd() {
status = '💨 Gone!';
}
</script>
<p>Status: {status}</p>
<button on:click={() => visible = !visible}>
Toggle
</button>
{#if visible}
<div
transition:fade
on:introstart={handleIntroStart}
on:introend={handleIntroEnd}
on:outrostart={handleOutroStart}
on:outroend={handleOutroEnd}
>
Watch me animate!
</div>
{/if}
🎯 Real World Uses
1. Disable buttons during animation:
<script>
let visible = true;
let animating = false;
</script>
<button
disabled={animating}
on:click={() => visible = !visible}
>
{animating ? 'Wait...' : 'Toggle'}
</button>
{#if visible}
<div
transition:fade
on:introstart={() => animating = true}
on:introend={() => animating = false}
on:outrostart={() => animating = true}
on:outroend={() => animating = false}
>
Content
</div>
{/if}
2. Chain animations:
<script>
let step = 1;
</script>
{#if step === 1}
<div
transition:fade
on:outroend={() => step = 2}
>
Step 1: I go first
</div>
{:else}
<div transition:fade>
Step 2: Now I appear!
</div>
{/if}
3. Play sounds or effects:
<div
transition:fly={{ y: 100 }}
on:introend={() => playSound('whoosh')}
>
🚀 Rocket launches with sound!
</div>
🎁 Putting It All Together
Here’s a complete example using all three concepts:
<script>
import { crossfade } from 'svelte/transition';
// Custom elastic transition
function elastic(node, { duration = 400 }) {
return {
duration,
css: (t) => {
const bounce = Math.sin(t * Math.PI * 3)
* (1 - t);
return `transform: scale(${t + bounce * 0.3})`;
}
};
}
// Crossfade setup
const [send, receive] = crossfade({
duration: 300,
fallback: elastic
});
let items = [
{ id: 1, name: 'Apple', cart: false },
{ id: 2, name: 'Banana', cart: false }
];
let message = '';
function moveToCart(id) {
items = items.map(i =>
i.id === id ? {...i, cart: true} : i
);
}
</script>
<p>{message}</p>
<div class="shop">
<h3>Shop</h3>
{#each items.filter(i => !i.cart) as item (item.id)}
<button
in:receive={{ key: item.id }}
out:send={{ key: item.id }}
on:click={() => moveToCart(item.id)}
on:outrostart={() => message = 'Moving...'}
on:outroend={() => message = 'In cart!'}
>
{item.name}
</button>
{/each}
</div>
<div class="cart">
<h3>Cart</h3>
{#each items.filter(i => i.cart) as item (item.id)}
<span
in:receive={{ key: item.id }}
out:send={{ key: item.id }}
>
{item.name}
</span>
{/each}
</div>
🏆 Quick Reference
| Feature | What | When To Use |
|---|---|---|
| Custom Transition | Build your own animation | Need unique effects |
| Crossfade | Items swap places | Moving items between lists |
| Transition Events | Know when animations happen | Chain effects, disable UI |
🌈 You Did It!
You now know how to:
✅ Create custom transitions with your own animations ✅ Use crossfade to make items fly between locations ✅ Listen to events to know when animations start and end
You’re not just using transitions anymore—you’re creating magic! ✨
“The best animations don’t just move things. They tell a story.”
