Transition & Animation — Vue

Transition Vue menyediakan komponen untuk animasi enter/leave: const show = ref(true) ---template--- Toggle Animated! CSS yang dibutuhkan: .fade-enter-active…

<Transition> menganimasikan satu elemen saat masuk ke halaman atau keluar dari halaman, dan <TransitionGroup> melakukannya untuk setiap item sebuah daftar. Vue sendiri gak menggambar animasinya. Dia memasang class CSS di setiap tahap, CSS-mu yang menggerakkan, dan saat elemen keluar, Vue menunggu gerakannya selesai sebelum membuangnya. Bayangin papan kecil "Masuk keranjang!" di Warung Bu Sri. Kalau papannya langsung muncul lalu langsung hilang, pembeli gak sempat sadar. Jadi Bu Sri mengangkatnya pelan-pelan, menahannya sebentar, menurunkannya pelan-pelan, dan baru menyimpannya setelah papannya benar-benar turun.

🧪 Tebakan cepat: <p v-if="tampil"> dibungkus <Transition name="fade">, dengan CSS fade 0,3 detik. Begitu tampil jadi false, berapa lama <p> itu masih ada di DOM: langsung hilang, atau sekitar 0,3 detik? Tebak dulu, jawabannya ada di bagian "Class di setiap tahap".

Transition: satu elemen masuk dan keluar

Toast "Masuk keranjang" yang muncul tiap kali Tambah diklik, lalu hilang sendiri setelah 2 detik:

<script setup>
import { ref } from "vue"

const toast = ref("")
let timer

function tambah(nama) {
  toast.value = "Masuk keranjang: " + nama
  clearTimeout(timer)
  timer = setTimeout(() => (toast.value = ""), 2000)
}
</script>

<template>
  <button @click="tambah('Nasi Goreng')">Tambah Nasi Goreng</button>

  <Transition name="fade">
    <p v-if="toast" class="toast">{{ toast }}</p>
  </Transition>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

Class di setiap tahap

Ini urutan class di <p> yang tercatat di Chrome dengan CSS di atas:

Waktu Kejadian Class di <p>
0 ms toast terisi, <p> dimasukkan ke DOM fade-enter-from fade-enter-active
1 frame fade-enter-active fade-enter-to
300 ms transisinya selesai class transisi dilepas
2 detik toast dikosongkan fade-leave-from fade-leave-active
1 frame fade-leave-active fade-leave-to
300 ms kemudian <p> dibuang dari DOM

mode="out-in": ganti satu elemen dengan yang lain

Kalau isi toast berganti dari Nasi Goreng ke Es Teh dengan <p :key="toast">, Vue menganggapnya elemen baru: yang lama keluar, yang baru masuk. Defaultnya keduanya jalan bersamaan, jadi selama 300 ms ada dua <p> di DOM dan tata letaknya melompat. <Transition name="fade" mode="out-in"> membuat yang lama keluar dulu sampai selesai, baru yang baru masuk.

v-if dan v-else di dalam satu <Transition> juga boleh, karena yang dirender tetap cuma satu.

TransitionGroup: daftar keranjang

<TransitionGroup name="list" tag="ul">
  <li v-for="item in keranjang" :key="item.id">{{ item.nama }}</li>
</TransitionGroup>
.list-enter-active,
.list-leave-active {
  transition: all 0.3s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
.list-move {
  transition: transform 0.3s ease;
}
.list-leave-active {
  position: absolute;
}

Keranjang berisi Nasi Goreng, Mie Ayam, Es Teh, lalu Nasi Goreng dihapus:

:key Yang terlihat selama 300 ms
item.id Nasi Goreng memudar ke kanan, Mie Ayam dan Es Teh bergeser naik dengan list-move
gak ada baris pertama langsung jadi Mie Ayam, baris kedua jadi Es Teh, dan baris ketiga yang memudar

Tanpa key, Vue memakai ulang elemen menurut urutan, mengganti teksnya, lalu membuang elemen terakhir. Animasinya jalan di baris yang salah.

Di editor latihan

Latihan pertama ditulis sebagai file .vue utuh, seperti di proyek Vite: <script setup>, <template>, dan <style>, dan CSS di <style> ikut dipasang di pratinjau. Format ---template--- yang dipakai Playground di Lab gak punya blok <style>: <style> di template dibuang diam-diam. Karena itu kode Playground di Lab memasang CSS-nya lewat script:

document.getElementById("gaya-transisi")?.remove()
const gaya = document.createElement("style")
gaya.id = "gaya-transisi"
gaya.textContent = ".fade-enter-active, .fade-leave-active { transition: opacity 0.3s } .fade-enter-from, .fade-leave-to { opacity: 0 }"
document.head.appendChild(gaya)

⚠️ Jebakan yang sering ketemu

TL;DR: <Transition name="fade"> membungkus satu elemen yang muncul dan hilang lewat v-if, v-show, atau :key. Vue memasang fade-enter-from, fade-enter-active, fade-enter-to saat masuk dan fade-leave-from, fade-leave-active, fade-leave-to saat keluar; CSS-mu yang menggerakkan, dan elemen yang keluar baru dibuang setelah transisinya selesai. mode="out-in" membuat yang lama keluar dulu sebelum yang baru masuk. <TransitionGroup tag="ul"> menganimasikan setiap item daftar, wajib dengan :key unik, dan class -move menggeser item yang berpindah. Taruh v-if di elemen, bukan di Transition-nya.

Yang akan kamu pelajari