Advanced Svelte Transition Features
Tags: svelte, webdev, programming, javascript
Published: 2021-01-28 21:42:32

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**


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.