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>diberianimate: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 |
flipdiimpor dari"svelte/animate", bukan dari"svelte/transition".animate:flipdipasang di elemen yang langsung berada di dalam{#each}dengan key, di sini(m.nama).- Animasinya cuma jalan saat urutan berubah: diurutkan, dibalik, atau dipindah. Menambah dan menghapus baris adalah urusan transisi.
keranjang = [...keranjang].sort(...)membuat array baru lalu menugaskannya. Di Svelte 4,keranjang.sort(...)saja mengubah array-nya tanpa assignment, jadi tampilannya gak ikut berubah.
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 |
- Baris yang pindah jauh bergerak lebih lama, tapi gak dua kali lipat: jarak dua kali, durasinya cuma sekitar 1,4 kali.
{{ duration: 300 }}membuat semua baris bergerak 300 ms, sejauh apa pun.durationjuga boleh function:{{ duration: (d) => d * 3 }}.- Untuk pengguna yang menyalakan "kurangi gerakan", pakai cara dari pelajaran Transitions:
{{ duration: kurangiGerak ? 0 : 300 }}.
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:
- Elemennya harus anak langsung
{#each}:<div><li animate:flip>ditolak denganmust be the immediate child of a keyed each block. - Elemennya harus satu-satunya anak di dalam
{#each}:<li animate:flip>ditambah<span>di sebelahnya ditolak denganmust be the sole child of a keyed each block.
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
- Lupa key.
{#each keranjang as m}dengananimate:flipditolak compiler. Tulis(m.nama). - Key index.
{#each keranjang as m, i (i)}lolos compile, tapi key-nya ikut posisi, jadi elemennya diam di tempat dan cuma teksnya yang berganti. Gak ada yang meluncur. - Sort tanpa assignment. Di Svelte 4,
keranjang.sort(...)saja gak mengubah tampilan. Tugaskan hasilnya:keranjang = [...keranjang].sort(...). - Yang dicek latihannya.
animate:flipdi kodemu, minimal 5<div>, sebuah<button>denganon:click, dan urutan yang benar-benar berubah lewatsort(,reverse(, ataushuffle.
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.