Custom Transitions And Staggered Transitions In Svelte(with AnimeJs)
Tags: svelte, beginners, webdev, animations
Published: 2019-11-01 16:43:27


Let's start with the obvious.

#Method #1
The first method will obviously to make use of the built-in transitions from **svelte/transition** and delay each element by its index.

Here's an example using the **fly** transition,
again this method is not limited to just the fly transition,you can use any transition from **svelte/transition** 
```html
<script>
import { fly } from "svelte/transition";
	let cats = [
	  { id: 1, name: "Keyboard Cat" },
	  { id: 2, name: "Maru" },
	  { id: 3, name: "Henri The Existential Cat" }
	];
let visible = false;

</script>

<input type="checkbox" bind:checked={visible}>
<ul>
{#each cats as {id,name},i}
  {#if visible}
    <h1 in:fly="{{x: -200,duration: 1000,delay:i*200 }}" 
out:fly="{{ x: 200, duration: 1000,delay:i*200 }}">{i +1}.{name}</h1>
  {/if}
{/each}
</ul>
```
But this method has some obvious limitations,you can only use it to transition one property at a time.


#Method #2
To overcome the limitations of **method 1#** we can create our own custom transition.
for this example i modified the **spin** custom animation from the svelte official tutorial.

```html
<script>
	import { fly } from "svelte/transition";
	import { elasticOut } from "svelte/easing";
	let cats = [
	  { id: 1, name: "Keyboard Cat" },
	  { id: 2, name: "Maru" },
	  { id: 3, name: "Henri The Existential Cat" }
	];
	let visible = false;

	function spin(node, { duration, delay }) {
	  return {
	    delay,
	    duration,
	    css: t => {
	      const eased = elasticOut(t);
	      return `transform: scale(${eased}) rotate(${eased * 360}deg);`
	    }
	  };
	}
</script>

<input type="checkbox" bind:checked={visible}>
<ul>
{#each cats as {name},i}
  {#if visible}
    <h1 in:spin="{{duration: 1000,delay:i*200 }}" 
out:fly="{{ x: 200, duration: 1000,delay:i*200 }}">{i +1}.{name}</h1>
  {/if}
{/each}
</ul>
```
Let's break this down starting with **spin(node, { duration, delay })**

**node** - refers to the element being transitioned
**duration** -refers to the time it will take to complete the transition
**delay** - refers the time between transitions

the css function returns a css animation and has parameters **t** and **u**
_**t** value is 0 at the beginning of an intro or the end of an outro, and 1 at the end of an intro or beginning of an outro,and **u** its the opposite of **t**_

so our scale property is transitioned from 0 to 1



#Method #3

the first two methods are great for simple transitions,for complicated animations we will need something extra,that extra **something something** for me is **AnimeJs**

 
Let's start by installing animejs
```bash
npm install animejs --save
```

Now lets modify our code once more
```html
<script>
	import anime from "animejs/lib/anime.es.js";
	let cats = [
	  { id: 1, name: "Keyboard Cat" },
	  { id: 2, name: "Maru" },
	  { id: 3, name: "Henri The Existential Cat" }
	];
	let visible = false;

    function reverse(node, { targets, duration }) {
    return {
      css: t => {
        return anime({
          targets,
          duration,
          easing: "easeInOutCirc",
          opacity: [1, 0],
          translateX: [0, -500],
          scale: [1, 0],
          delay: anime.stagger(200),
        });
      }
    };
    
  }
	function forward(node, { targets, duration }) {
    return {
      css: t => {
        anime({
          targets,
          duration,
          easing: "easeInOutCirc",
          opacity: [0, 1],
          translateX: [-500, 0],
          scale: [0, 1],
          delay: anime.stagger(200)
        });
      }
    };
    
  }
</script>

<input type="checkbox" bind:checked={visible}>
<ul>
{#each cats as {name},i}
  {#if visible}
    <h1 class="animate"
         in:forward="{{ targets: '.animate', duration: 1500 }}"
         out:reverse="{{ targets: '.animate', duration: 1500 }}">
         {i +1}.{name} 
    </h1>
  {/if}
{/each}
</ul>
```
In this example i made two transitions instead of one,
 

and as you might have noticed i used targets to refer to the elements instead of node,this is due to the fact that node only refers to one element.
And another confusing thing is that i used **css function** instead of the **tick function**,this was due to the performance issues i got by using tick and animejs seems to return css animations this might explain why this works(don't qoute me on this)

---
Thanks for reading and stay tuned!