DEV Community

Khutso siema
Khutso siema

Posted on

Custom Transitions And Staggered Transitions In Svelte(with AnimeJs)

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

<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>
Enter fullscreen mode Exit fullscreen mode

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.

<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>
Enter fullscreen mode Exit fullscreen mode

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

npm install animejs --save
Enter fullscreen mode Exit fullscreen mode

Now lets modify our code once more

<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>
Enter fullscreen mode Exit fullscreen mode

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!

Oldest comments (2)

Collapse
 
irvingarmenta profile image
Irving Armenta

Thanks for the article!
I'm starting to use Svelte to make my personal page, very simple one.

I'm used to using AnimeJS on React, and there are some caveats that I know how to fix in React, but in svelte I'm not sure.

it seems that there is no timeout for the transition, so when triggering the "reverse" function animation it does not wait for the animation to be completed to hide it, I've been looking in Svelte docs but it seems there is no way to add a timeout, do you know how to solve this?
Thanks!

Collapse
 
srinirg profile image
srini-rg

This is some landmark discovery, bro. I mean, using Anime JS in the Svelte transition callback. That is going to make Svelte transitions a lot more flexible. If at all possible, do please investigate more on this. I too, will try.