Animations Svelte — Svelte

animate: directive digunakan untuk menganimasikan elemen saat posisinya berubah di dalam {#each}. animate:flip import { flip } from "svelte/animate" let items…

animate: adalah directive yang menganimasikan perpindahan posisi elemen di dalam {#each} yang punya key, saat urutan daftarnya berubah. Yang paling sering dipakai animate:flip: baris keranjang yang diurutkan ulang meluncur ke tempat barunya, bukan tiba-tiba lompat. Bayangin Bu Sri menata ulang piring di etalase Warung Bu Sri. Dia gak mengangkat semua piring lalu menaruhnya sekaligus; tiap piring digeser ke tempat barunya, jadi pembeli bisa mengikuti piring mana pindah ke mana. Bu Sri bisa begitu karena dia kenal tiap piring dari isinya. Di Svelte, "kenal" itu adalah key.

🧪 Tebakan cepat: keranjang digambar dengan {#each keranjang as m}, tanpa key, dan tiap <li> diberi animate:flip. Saat tombol Urutkan termurah diklik, barisnya meluncur, lompat, atau gak jalan sama sekali? Jawabannya ada di bagian "Wajib keyed each".

animate:flip di daftar keranjang

<script>
  import { flip } from "svelte/animate"

  let keranjang = [
    { nama: "Nasi Goreng", harga: 15000, porsi: 2 },
    { nama: "Mie Ayam", harga: 12000, porsi: 1 },
    { nama: "Es Teh", harga: 5000, porsi: 3 },
  ]

  function urutkanTermurah() {
    keranjang = [...keranjang].sort((a, b) => a.harga - b.harga)
  }
</script>

<button on:click={urutkanTermurah}>Urutkan termurah</button>

<ul>
  {#each keranjang as m (m.nama)}
    <li animate:flip={{ duration: 300 }}>{m.nama} x{m.porsi}</li>
  {/each}
</ul>

Tiap baris tingginya 32 px. Ini yang terjadi saat tombolnya diklik:

Baris Sebelum Sesudah Bergeser
Nasi Goreng ke-1 (0 px) ke-3 (64 px) turun 64 px dalam 300 ms
Mie Ayam ke-2 (32 px) ke-2 (32 px) diam
Es Teh ke-3 (64 px) ke-1 (0 px) naik 64 px dalam 300 ms

Apa itu FLIP

FLIP adalah singkatan dari First, Last, Invert, Play, empat langkah yang Svelte jalankan untuk tiap baris:

Langkah Artinya Untuk Es Teh
First catat posisi sebelum berubah top 64 px
Last biarkan DOM berpindah, catat posisi barunya top 0 px
Invert geser balik ke posisi lama dengan transform translate(0, 64px)
Play animasikan transform itu ke 0 64 px → 0 dalam 300 ms

DOM-nya sudah pindah sejak awal; yang dianimasikan cuma transform. Itu ringan untuk browser, karena tata letak halaman gak dihitung ulang di tiap frame.

Parameter dan durasi bawaan

flip menerima delay (bawaan 0), easing (bawaan cubicOut), dan duration. Kalau duration gak diisi, Svelte menghitungnya dari jarak geser d dalam piksel: Math.sqrt(d) * 120.

Jarak geser Durasi bawaan
32 px 679 ms
64 px 960 ms
128 px 1.358 ms

Wajib keyed each

Tanpa key, kode di tebakan ditolak compiler:

An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?

Jadi gak meluncur dan gak lompat: komponennya gak jadi sama sekali. Itu jawaban tebakannya. Alasannya ada di pelajaran List Rendering: tanpa key, Svelte memakai ulang elemen berdasarkan posisi dan cuma mengganti teksnya, jadi gak ada elemen yang benar-benar pindah untuk dianimasikan.

Dua aturan lain, juga dengan pesan compiler-nya sendiri:

Digabung dengan transisi

{#each keranjang as m (m.nama)}
  <li animate:flip={{ duration: 300 }} transition:fade={{ duration: 200 }}>
    {m.nama} x{m.porsi}
  </li>
{/each}

Saat Mie Ayam dihapus, barisnya memudar 200 ms (transisi), dan Es Teh di bawahnya naik ke tempat yang kosong (flip). Selama memudar, Svelte membuat baris yang keluar itu position: absolute, jadi baris lain bisa langsung bergeser.

Latihan: mengacak angka

<script>
  import { flip } from "svelte/animate"

  let items = [1, 2, 3, 4, 5]

  function shuffle() {
    items = items.sort(() => Math.random() - 0.5)
  }
</script>

<button on:click={shuffle}>Acak</button>

{#each items as item (item)}
  <div animate:flip={{ duration: 300 }}>{item}</div>
{/each}

items.sort(...) mengacak array-nya di tempat, dan items = ... menugaskannya kembali, jadi Svelte melihat urutan baru. Key-nya (item), angkanya sendiri, karena kelima angka itu unik.

Svelte 5

animate:flip sama di Svelte 5, dan tetap wajib di dalam keyed each. Bedanya, array $state ikut reaktif saat diubah langsung, jadi keranjang.sort((a, b) => a.harga - b.harga) tanpa assignment sudah cukup.

⚠️ Jebakan yang sering ketemu

TL;DR: animate:flip (dari "svelte/animate") membuat elemen di dalam keyed each meluncur ke posisi barunya saat urutan array berubah. FLIP berarti First, Last, Invert, Play: catat posisi lama, biarkan DOM pindah, geser balik dengan transform, lalu animasikan ke 0. Tanpa duration, lamanya Math.sqrt(d) * 120 ms menurut jarak geser; {{ duration: 300 }} menyamakannya. animate: wajib di anak langsung dan satu-satunya dari {#each ... (key)}, kalau gak, compiler menolak; key index gak menghasilkan gerakan. Tambah dan hapus baris diurus transisi, dan keduanya bisa dipasang bersamaan. Di Svelte 4, urutan baru butuh assignment. Latihannya: import { flip } from "svelte/animate" dan <div animate:flip={{ duration: 300 }}>{item}</div> di dalam {#each items as item (item)}, dengan tombol Acak.

Yang akan kamu pelajari