Kurs Svelte i SvelteKit · Moduł 9: Przejścia i animacje

Animacje list (animate:flip)

2 min czytania ·
W tej lekcji3

animate:flip

Animuj zmiany pozycji elementów w liście:

<script>
  import { flip } from 'svelte/animate';
  import { fade } from 'svelte/transition';

  let items = $state([
    { id: 1, name: 'Ragnar' },
    { id: 2, name: 'Bjorn' },
    { id: 3, name: 'Ivar' }
  ]);

  function shuffle() {
    // $state to głęboki proxy - sort() w miejscu też odświeża listę
    items.sort(() => Math.random() - 0.5);
  }
</script>

<button onclick={shuffle}>Przetasuj</button>

<ul>
  {#each items as item (item.id)}
    <li
      animate:flip={{ duration: 300 }}
      transition:fade
    >
      {item.name}
    </li>
  {/each}
</ul>

Przykład: Sortowalna lista

<script>
  import { flip } from 'svelte/animate';

  let warriors = $state([
    { id: 1, name: 'Ragnar', strength: 95 },
    { id: 2, name: 'Bjorn', strength: 88 },
    { id: 3, name: 'Ivar', strength: 70 }
  ]);

  let sortBy = $state('name');

  let sorted = $derived(
    [...warriors].sort((a, b) =>
      sortBy === 'name'
        ? a.name.localeCompare(b.name)
        : b.strength - a.strength
    )
  );
</script>

<select bind:value={sortBy}>
  <option value="name">Po imieniu</option>
  <option value="strength">Po sile</option>
</select>

<ul>
  {#each sorted as warrior (warrior.id)}
    <li animate:flip={{ duration: 300 }}>
      {warrior.name} - {warrior.strength}
    </li>
  {/each}
</ul>

crossfade - Przejścia między listami

<script>
  import { crossfade, fade } from 'svelte/transition';
  import { flip } from 'svelte/animate';

  const [send, receive] = crossfade({ fallback: fade });

  let left = $state([1, 2, 3]);
  let right = $state([4, 5]);

  function moveRight(item) {
    left = left.filter(i => i !== item);
    right = [...right, item];
  }
</script>

<div class="lists">
  <ul>
    {#each left as item (item)}
      <li
        in:receive={{ key: item }}
        out:send={{ key: item }}
        animate:flip
        onclick={() => moveRight(item)}
      >
        {item}
      </li>
    {/each}
  </ul>

  <ul>
    {#each right as item (item)}
      <li
        in:receive={{ key: item }}
        out:send={{ key: item }}
        animate:flip
      >
        {item}
      </li>
    {/each}
  </ul>
</div>
Kod do tej lekcji: App.svelte
<script>
  // animate: i flip - płynne przesuwanie elementów
  import { flip } from 'svelte/animate';
  import { fade } from 'svelte/transition';

  let items = $state([
    { id: 1, name: 'Ragnar', rank: 1 },
    { id: 2, name: 'Bjorn', rank: 2 },
    { id: 3, name: 'Ivar', rank: 3 },
    { id: 4, name: 'Ubbe', rank: 4 },
    { id: 5, name: 'Hvitserk', rank: 5 }
  ]);

  function shuffle() {
    items = items
      .map(item => ({ ...item, sort: Math.random() }))
      .sort((a, b) => a.sort - b.sort)
      .map((item, i) => ({ ...item, rank: i + 1 }));
  }

  function sortByName() {
    items = [...items]
      .sort((a, b) => a.name.localeCompare(b.name))
      .map((item, i) => ({ ...item, rank: i + 1 }));
  }
</script>

<main class="flip-demo">
  <h1>animate:flip</h1>
  <p class="subtitle">Płynne przesuwanie elementów listy</p>

  <div class="controls">
    <button onclick={shuffle}>Losuj</button>
    <button onclick={sortByName}>Sortuj A-Z</button>
  </div>

  <div class="list">
    {#each items as item (item.id)}
      <div
        class="item"
        animate:flip={{ duration: 300 }}
        transition:fade
      >
        <span class="rank">#{item.rank}</span>
        <span class="name">{item.name}</span>
      </div>
    {/each}
  </div>
</main>

<style>
  .flip-demo {
    font-family: 'Georgia', serif;
    max-width: 400px;
    margin: 0 auto;
    padding: 2rem;
    background: #1a0a0a;
    color: #f7d5e8;
    min-height: 100vh;
  }

  h1 {
    color: #ff6b6b;
    text-align: center;
  }

  .subtitle {
    text-align: center;
    color: #a87ca8;
  }

  .controls {
    display: flex;
    gap: 0.5rem;
    margin: 1rem 0;
  }

  button {
    flex: 1;
    padding: 0.75rem;
    background: #3a1a2a;
    color: #f7d5e8;
    border: 1px solid #ff6b6b;
    border-radius: 4px;
    cursor: pointer;
  }

  .list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  .item {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem;
    background: rgba(255, 107, 107, 0.1);
    border: 1px solid #ff6b6b;
    border-radius: 8px;
  }

  .rank {
    font-size: 1.5rem;
    color: #ffd700;
    min-width: 50px;
  }

  .name {
    font-size: 1.1rem;
  }
</style>

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Do czego służy animate:flip?

Przydatne artykuły