skip to Main Content

I was following the Svelte tutorial, more specifically the part about REST params, I tried to animate the whole thing; however, I only succeeded into animating what is inside the each block.
I wanted to use animate:flip on the <p> below the each block but it’s not possible since it can only be used on direct children of the previously mentioned block.

My question is, how can I animate the change of position like animate:flip would’ve done?

For reference, here’s the code that I modified in the tutorial.

<div class="flex">
    {#each words.slice(0, depth) as word (word)}
        <p animate:flip transition:fade>{word}</p>
    {/each}

    <p><a href={next}>{words[depth] ?? '?'}</a></p> <!-- I'd like to animate this -->
</div>

P.S.: Sorry if it’s an already answered question but I can’t find any proper way of doing so online.

2

Answers


  1. You could make the last element part of the each, that way the animation will be applied, e.g.

    {#each Array.from({ length: depth + 1 }) as _, i (i)}
        {@const word = words[i]}
        <p animate:flip transition:fade>
            {#if i == depth}
                <a href={next}>{words[depth] ?? '?'}</a>
            {:else}
                {word}
            {/if}
        </p>
    {/each}
    

    (Not looping over words.slice so it can read over the bounds for parity with the example. Otherwise you would never get a ? entry.)

    Login or Signup to reply.
  2. You can make more custom transitions with crossfade (svelte/transition), though this has its own issues.

    For one, while the element moves to its new position in the list, the old space will be occupied in a flex layout and the new word will appear shifted down. I tried to exclude the item from layout via the transition events, but they either did not fire reliably or I could not access the correct element.

    One workaround would be to delay the appearance of the new word until the crossfade is over and the empty spot is removed which is not ideal either.

    Also, it does not quite work as envisioned in the opposite direction (in the given example that could be triggered by browser back navigation) and the fade out at the end of the stack somehow breaks the layout.

    <script>
      // ...
      const duration = 250;
      const [send, receive] = crossfade({
        duration,
        fallback: n => fade(n, {
          duration,
          delay: duration,
        }),
      });
    </script>
    
    <div class="flex">
      {#each words.slice(0, depth) as word, i (i)}
        <p animate:flip
          in:receive={{ key: i }}
          out:send={{ key: i }}>
          {word}
        </p>
      {/each}
    
      {#key depth}
        <p out:send={{ key: depth - 1 }} in:receive={{ key: depth }}>
          <a href={next}>{words[depth] ?? '?'}</a>
        </p>
      {/key}
    </div>
    

    If you manage to improve on this, please add your solution as an answer or suggest an edit (if you can).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search