Svelte is an up and coming front end framework for developing front end web apps.
It’s simple to use and lets us create results fast.
In this article, we’ll look at how to use advance Svelte transition features, including transition events, and local and deferred transitions.
Transition Events
We can add event handlers for various transition events to do things like logging.
For instance, we can write the following code to add an animation for our p element when it’s being toggled on and off and show the status of the animation:
App.svelte
:
<script>
import { fly } from "svelte/transition";
let visible = true;
let status = "";
</script>
<button on:click={() => visible = !visible}>Toggle</button>
<p>Animation status: {status}</p>
{#if visible}
<p
transition:fly="{{ y: 200, duration: 2000 }}"
on:introstart="{() => status = 'intro started'}"
on:outrostart="{() => status = 'outro started'}"
on:introend="{() => status = 'intro ended'}"
on:outroend="{() => status = 'outro ended'}"
>
foo
</p>
{/if}
In the code above, we add event handlers for introstart
, outrostart
, introend
, and outroend
events which set the status
.
Then we display the status
in a p tag. As we click the Toggle button, we’ll see the animation happen and the status
being updated.
Local Transitions
We can apply transitions to individual list elements with local transitions. For instance, we can write the following to add a slide
effect to each item on a list:
App.svelte
:
<script>
import { slide } from "svelte/transition";
let items = [];
const addItem = () => {
items = [...items, Math.random()];
};
</script>
<button on:click={addItem}>Add Item</button>
{#each items as item}
<div transition:slide>
{item}
</div>
{/each}
In the code, we have the addItem
function to add a new number to an array. Then we display the array in the markup.
In the div inside the each
, we add the transition:slide
directive to apply a slide effect when the item is added.
Therefore, we’ll see the slide effect as we click Add Item.
Deferred Transitions
With Svelte, we can defer transitions so that we can coordinate animation between multiple elements.
We can use the crossfade
function to achieve this. It creates a pair of transitions called send
and receive
.
If the element is sent, then it looks for a corresponding element being received and generates a transition that transforms the element to its counterpart’s position and fades it out.
When an element is received, then the opposite happens. If there’s no counterpart, then the fallback
transition is used.
For instance, we can animate the moving of items between 2 lists as follows:
App.svelte
:
<script>
import { cubicOut } from "svelte/easing";
import { crossfade } from "svelte/transition";
const [send, receive] = crossfade({
duration: d => Math.sqrt(d * 200),
fallback(node, params) {
const style = getComputedStyle(node);
const transform = style.transform === "none" ? "" : style.transform;
return {
duration: 600,
easing: cubicOut,
css: t => `
transform: ${transform} scale(${t});
opacity: ${t}
`
};
}
});
let uid = 1;
let todos = [
{ id: uid++, done: false, description: "write some docs" },
{ id: uid++, done: false, description: "eat" },
{ id: uid++, done: true, description: "buy milk" },
{ id: uid++, done: false, description: "drink" },
{ id: uid++, done: false, description: "sleep" },
{ id: uid++, done: false, description: "fix some bugs" }
];
const mark = (id, done) => {
const index = todos.findIndex(t => t.id === id);
todos[index].done = done;
};
</script>
<style>
#box {
display: flex;
flex-direction: row;
}
</style>
<div id='box'>
<div>
Done:
{#each todos.filter(t => t.done) as todo}
<div
in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}"
>
{todo.description}
<button on:click={mark(todo.id, false)}>Not Done</button>
</div>
{/each}
</div>
<div>
Not Done:
{#each todos.filter(t => !t.done) as todo}
<div
in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}"
>
{todo.description}
<button on:click={mark(todo.id, true)}> Done</button>
</div>
{/each}
</div>
</div>
In the code above, we created our send
and receive
transition functions with the crossfade
function.
The fallback
animation is used when there’s no receive
counterpart for the send
and vice versa, which can happen if we aren’t moving an element between 2 elements.
The fallback animation just does some scaling and opacity changes to the element being animated.
We render the 2 todo lists for done and not done items. When we click the Done or Not Done button beside the todo item, we’ll see a fade effect as the item is moved between the containing divs.
This is achieved with the in
and out
animations, we pass in an object with the key
property with the value set to the id
so that Svelte knows which element we’re moving.
Conclusion
We can use crossfade
to coordinate transitions when it involves multiple elements.
Also, we use local transitions to animate list items that are being rendered.
Finally, we can track transition effects by listening to the events that are emitted during transitions by attaching event listeners to each.