Pedoman animasi UX & performa 2025 — Ringkas dan praktis

Diterbitkan: 20 Sep 2025 · Waktu baca: 4 mnt · Redaksi Unified Image Tools

Mengapa tinggalkan GIF

GIF tidak efisien, kualitas rendah, dan terbatas warna. Tahun 2025: gunakan WebP/AVIF animasi untuk loop/UI, dan MP4/WebM pendek untuk video dengan audio/foto nyata.

Pemilihan format

  • Loop pendek / animasi UI: WebP/AVIF animasi
  • Rekaman nyata/foto + audio: MP4/WebM (video)
  • Butuh transparansi: WebP animasi (cek dukungan transparansi AVIF di browser)

Pola implementasi

<video autoplay loop muted playsinline width="640" height="360" preload="metadata">
  <source src="/loop.webm" type="video/webm" />
  <source src="/loop.mp4" type="video/mp4" />
  <!-- Gambar fallback -->
  <img src="/loop.jpg" alt="loop pendek" />
</video>

Tips perancangan pengalaman

  • Hormati prefers-reduced-motion (sediakan opsi berhenti via CSS/JS).
  • Jangan blok FCP/LCP: muat tepat sebelum interaksi.
  • Thumbnail sebagai gambar statis; saat diputar baru muat animasi/video.

Kurangi gerak via CSS

@media (prefers-reduced-motion: reduce) {
  .anim { animation: none !important; transition: none !important; }
}

Berpikir anggaran (performa & data)

  • Video hero di kisaran 500–800KB (atau poster statis + putar tertunda untuk kualitas).
  • Hindari banyak pemutaran bersamaan; fokus satu instance lewat aksi pengguna.
  • Mobile: anggap preload="none"; setel src setelah interaksi.

Checklist

  • [ ] Migrasi GIF → WebP/AVIF/video
  • [ ] Susun kebutuhan transparansi/loop
  • [ ] Dukung prefers-reduced-motion
  • [ ] Optimalkan lazy‑load dan cache

Studi kasus

  • Indikator loading: ganti dengan CSS animation, hemat ~30KB.
  • Hero animation jadi video: 2.4MB GIF → 420KB WebM (kualitas visual sebanding).
  • Pemicu saat scroll: injeksi src pada 30% terlihat (IntersectionObserver).

FAQ

  • Q: Lebar layar berapa video harus berhenti?

  • A: Pertimbangkan bandwidth/baterai; di mobile autoplay harus pendek dan tanpa suara. Putar penuh dengan aksi pengguna.

  • Q: WebP vs AVIF?

  • A: AVIF umumnya kompresi lebih baik; sertakan WebP untuk kompatibilitas.

  • Q: Dampak SEO?

  • A: Jangan merusak LCP/CLS. Hindari autoplay di hero; gunakan poster statis + putar tertunda.

  • Q: Subtitle/aksesibilitas?

  • A: Video bermakna perlu subtitle; dukung keyboard/stop/volume.

Prinsip desain — Gerak yang bermakna

  • Makna: batasi gerak untuk transisi status/sebab‑akibat/hirarki.
  • Aman: hindari kedipan >3Hz dan zoom/rotasi ekstrem (WCAG 2.3.1).
  • Konsisten: komponen yang sama → waktu/easing yang sama.
  • Dapat dibalik: jika gagal, UI statis tetap fungsional.

Waktu & easing — patokan

  • Transisi kecil: 120–180ms / cubic-bezier(0.2, 0, 0, 1)
  • Kontainer/modal: 180–240ms / ease-out
  • Transisi halaman (crossfade/slide): 240–320ms / ease-in-out
  • Pegas (fisik): damping: 20–28, stiffness: 140–220
.btn {
  transition: transform 160ms cubic-bezier(.2,0,0,1), box-shadow 160ms ease-out;
}
.btn:active { transform: translateY(1px); }

Web Animations API & pegas

// Hormati Reduced Motion saat memakai WAAPI
const prefersReduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
const el = document.querySelector('.card');
if (!prefersReduced && el) {
  el.animate([
    { transform: 'translateY(8px)', opacity: 0 },
    { transform: 'translateY(0)', opacity: 1 },
  ], {
    duration: 220,
    easing: 'cubic-bezier(.2,0,0,1)',
    fill: 'both'
  });
}
// Aproksimasi pegas sederhana
function springTo(el, to = 0, { stiffness = 180, damping = 22 } = {}) {
  let v = 0; // velocity
  let x = 1; // normalized position
  const dt = 1/60;
  function frame() {
    const f = -stiffness * (x - to) - damping * v;
    v += f * dt;
    x += v * dt;
    el.style.transform = `translateY(${x * 20}px)`;
    if (Math.abs(v) > 0.001 || Math.abs(x - to) > 0.001) requestAnimationFrame(frame);
  }
  frame();
}

Optimasi terkait scroll

  • Mulai pada 25–33% terlihat di viewport (IntersectionObserver).
  • Manfaatkan position: sticky; minimalisir perhitungan JS untuk parallax/pin.
  • Utamakan transform/opacity ketimbang properti mahal (top/left/width/height).
const io = new IntersectionObserver(entries => {
  entries.forEach(e => {
    if (e.isIntersecting) e.target.classList.add('in');
  });
}, { threshold: 0.3 });
document.querySelectorAll('.reveal').forEach(n => io.observe(n));

Lottie / CSS / Video — penggunaan

  • Lottie (JSON vektor): ikon UI/ilustrasi ringan; ganti warna/independen resolusi.
  • CSS: mikro‑interaksi; dependensi ringan, biaya eksekusi rendah.
  • Video: kualitas foto/ekspresi kompleks; kompresi efisien, subtitle/audio mungkin.

Aturan: “prioritas kualitas → Video”, “loop UI ringan → CSS/Lottie”, “butuh transparansi → Lottie/WebP animasi”.

Pengukuran performa

// Pahami dampak pada paint/compose
new PerformanceObserver((list) => {
  for (const e of list.getEntries()) {
    console.log(e.entryType, e.name, e.duration);
  }
}).observe({ entryTypes: ['longtask', 'measure'] });

performance.mark('anim:start');
// ... run animation ...
performance.mark('anim:end');
performance.measure('anim:duration', 'anim:start', 'anim:end');

Aksesibilitas/keamanan

  • Sediakan stop/jeda/sembunyikan (WCAG 2.2.2).
  • prefers-reduced-motion dengan alternatif; autoplay default tanpa suara.
  • Hindari kedipan 3Hz–60Hz; kendalikan kontras terang.

Implementasi tahan‑gagal — cek

  • [ ] Tujuan & KPI (CTR/durasi baca/selesai baca …)
  • [ ] Gerak hanya transform/opacity (tanpa reflow layout)
  • [ ] Lazy‑load/pemicu aksi pengguna
  • [ ] Aturan Reduced Motion & UI stop manual
  • [ ] Uji A/B untuk validasi efek

FAQ lanjutan

  • Q: Apakah Lottie bisa jadi berat?

    • A: Kurangi primitives dan optimalkan framerate; jika tetap berat, pindah ke video.
  • Q: Kapan GIF tak terhindarkan?

    • A: Hanya saat ada tuntutan kompatibilitas kuat. Sadari kompromi ukuran/kualitas dan batasi.
  • Q: Animasi terkait scroll terasa patah

    • A: Kombinasikan requestAnimationFrame dan IntersectionObserver, kurangi beban thread, dan jangan berlebihan memakai will-change.

Artikel terkait