Animasi keyframe

Aturan @keyframes, pengendalian iterasi, dan mode fill pada animasi.

Daftar isi

Animasi keyframe menjalankan urutan gaya yang ditetapkan dalam aturan @keyframes, dan urutan itu berjalan sendiri tanpa menunggu perubahan keadaan pada elemen. Topik ini membahas penulisan keyframe dengan kata kunci from dan to maupun persen, properti animation beserta shorthand-nya, serta pengendalian iterasi dan arah. Pembahasan dilanjutkan dengan perilaku animation-fill-mode, perbedaan animasi dengan transisi, serta pola nyata berupa pemberitahuan yang masuk dan indikator pemuatan.

Aturan @keyframes#

Aturan @keyframes mendefinisikan rangkaian keadaan yang dilalui sebuah animasi, dan setiap keadaan disebut keyframe. Keadaan ditulis dengan persen, sedangkan kata kunci from dan to merupakan singkatan dari 0% dan 100%. Di antara dua keyframe, browser menginterpolasi setiap properti yang nilainya berbeda, sehingga mekanismenya sama dengan interpolasi pada transisi.

Properti yang tidak disebutkan pada sebuah keyframe mempertahankan nilainya, sehingga keyframe hanya perlu menyatakan properti yang memang berubah. Nama animasi bersifat peka terhadap besar kecil huruf dan harus unik pada satu halaman. Apabila persen yang sama ditulis dua kali dalam satu aturan, yang digunakan adalah nilai yang muncul belakangan. Contoh berikut memuat dua aturan @keyframes, yaitu keyframe dua keadaan dengan kata kunci from dan to serta keyframe bertahap dengan persen:

css Contoh: keyframe dua keadaan dan keyframe bertahap
/* Dua keadaan dengan kata kunci from dan to */
@keyframes muncul {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Tiga keadaan dengan persen */
@keyframes berdenyut {
  0%   { opacity: 1;    transform: scale(1); }
  50%  { opacity: 0.35; transform: scale(0.8); }
  100% { opacity: 1;    transform: scale(1); }
}

Keyframe bertahap berguna ketika gerak yang diinginkan tidak berupa perpindahan lurus antara dua keadaan, misalnya denyut yang turun lalu naik kembali. Jumlah keyframe bebas, dan persennya tidak harus berjarak sama, sehingga ritme gerak dapat diatur sesuai kebutuhan.

Properti animation dan penulisan ringkasnya#

Keyframe tidak berjalan sebelum ada elemen yang merujuknya melalui properti animation. Properti animation-name menyebutkan nama yang dideklarasikan @keyframes, sedangkan animation-play-state menghentikan sementara dengan nilai paused dan melanjutkannya dengan running. Properti-properti dalam bentuk panjang mengendalikan seluruh aspek eksekusi, dan tabel berikut merangkum yang paling sering digunakan:

PropertiPeran
animation-nameNama keyframe yang dijalankan
animation-durationLama satu putaran
animation-timing-functionKurva kecepatan antar keyframe
animation-iteration-countJumlah putaran, termasuk infinite
animation-directionArah tiap putaran
animation-fill-modeGaya di luar rentang waktu animasi
animation-play-stateMenghentikan sementara dan melanjutkan

Shorthand animation merangkum ketujuh properti tersebut dalam satu deklarasi. Aturan urutannya sama dengan transisi, yaitu nilai waktu pertama dibaca sebagai durasi dan nilai waktu kedua sebagai delay. Delay berguna ketika animasi perlu menunggu, misalnya pemberitahuan yang baru masuk setengah detik setelah halaman terbuka, dan pasangannya animation-fill-mode: backwards menjaga keyframe pertama berlaku selama masa tunggu. Nama keyframe sebaiknya ditulis paling akhir, karena kata kunci lain yang sah pada shorthand, misalnya infinite atau reverse, diutamakan saat penguraian dan nama yang bertabrakan dengannya akan terabaikan:

css Contoh: menjalankan keyframe dengan shorthand animation
.pemberitahuan {
  /* Durasi 400 ms, kurva ease-out, mode pengisian both */
  animation: muncul 400ms ease-out both;
}

.titik-pemuatan {
  /* Durasi 1200 ms, kurva ease-in-out, berulang tanpa henti */
  animation: berdenyut 1200ms ease-in-out infinite;
}

Iterasi, arah, dan fill mode#

animation-iteration-count menetapkan jumlah putaran, baik berupa bilangan seperti 3 maupun kata kunci infinite untuk perulangan tanpa henti. animation-direction mengatur arah tiap putaran dengan nilai normal dan reverse, nilai alternate yang berputar maju dan mundur secara bergantian, serta nilai alternate-reverse yang memulai dari arah mundur. Pasangan alternate dengan jumlah putaran ganjil menghasilkan gerak bolak-balik yang mulus, karena akhir satu putaran bertepatan dengan awal putaran berikutnya.

animation-fill-mode menentukan gaya elemen di luar rentang waktu animasi, yaitu sebelum dimulai dan sesudah selesai. Nilai none menggunakan gaya biasa pada kedua sisi, forwards mempertahankan gaya keyframe terakhir setelah selesai, backwards menerapkan gaya keyframe pertama selama delay berlangsung, dan both menggabungkan keduanya. Mode inilah yang menyelamatkan animasi pembuka, karena tanpa forwards elemen kembali ke gaya semula begitu animasinya berakhir.

Perbedaan animasi dan transisi#

Transisi dan animasi menginterpolasi nilai properti dengan mekanisme yang sama, tetapi keduanya berbeda pemicu dan kemampuannya. Transisi menunggu perubahan nilai, sedangkan animasi berjalan segera setelah deklarasinya berlaku, termasuk ketika halaman baru dimuat. Tabel berikut merangkum perbedaan yang menentukan pilihan:

AspekTransisiAnimasi
PemicuPerubahan nilai propertiDeklarasi animation yang berlaku
Keadaan yang dilaluiDua keadaan, awal dan akhirBanyak keyframe sesuai persen
PerulanganTidak didukungDidukung melalui animation-iteration-count
Rentang waktuSatu durasi dan delaySatu durasi per putaran

Keduanya dapat digunakan berdampingan pada elemen yang sama, misalnya indikator yang berdenyut terus melalui animasi, kemudian warnanya bertransisi ketika proses yang ditunggu selesai. Kaidah pemilihannya sederhana, yaitu perubahan yang harus merespons interaksi cocok dengan transisi, sedangkan urutan gerak yang berjalan sendiri cocok dengan animasi.

Kelemahan masing-masing mengikuti kekuatannya. Transisi yang menunggu perubahan tidak dapat berjalan ketika halaman baru dimuat, sedangkan animasi yang berjalan sendiri kurang tepat untuk merespons interaksi karena pemicunya tidak terkait keadaan elemen. Menetapkan pemicunya terlebih dahulu umumnya sudah menunjukkan pilihan yang tepat.

Pola pemberitahuan dan indikator pemuatan#

Dua pola berikut muncul hampir di setiap aplikasi. Pemberitahuan masuk dianimasikan satu putaran dengan mode pengisian both, sehingga elemen tetap berada pada keadaan akhirnya setelah muncul. Indikator pemuatan berupa titik kecil yang berdenyut dengan infinite, karena keadaan selesainya memang belum diketahui. Demo berikut memperlihatkan keduanya sekaligus, dan halaman demo perlu dimuat ulang untuk melihat pemberitahuannya masuk lagi:

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Demo pemberitahuan dan indikator pemuatan</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <main>
    <h1>Demo animasi keyframe</h1>

    <p class="pemberitahuan">Catatan tersimpan</p>

    <p class="indikator"><span class="titik"></span>Menyimpan catatan</p>
  </main>

<style>
/* Gaya demo pemberitahuan yang masuk dan indikator pemuatan */
body {
  font-family: sans-serif;
  background-color: #eef2f7;
  color: #1f2937;
  margin: 0;
  padding: 16px;
}

h1 { font-size: 20px; margin: 0 0 12px; }

/* Pemberitahuan masuk: satu putaran, keadaan akhirnya bertahan */
.pemberitahuan {
  display: inline-block;
  margin: 0;
  padding: 12px 18px;
  background-color: #ffffff;
  border: 1px solid #cbd5e1;
  border-left: 4px solid #16a34a;
  border-radius: 8px;
  font-size: 14px;
  animation: muncul 400ms ease-out both;
}

/* Indikator kecil: titik yang berdenyut tanpa henti */
.indikator {
  margin: 16px 0 0;
  font-size: 13px;
  color: #475569;
}

.titik {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 6px;
  border-radius: 50%;
  background-color: #2563eb;
  vertical-align: middle;
  animation: berdenyut 1200ms ease-in-out infinite;
}

@keyframes muncul {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes berdenyut {
  0%   { opacity: 1;    transform: scale(1); }
  50%  { opacity: 0.35; transform: scale(0.8); }
  100% { opacity: 1;    transform: scale(1); }
}

</style>
<style>
/* Gaya demo pemberitahuan yang masuk dan indikator pemuatan */
body {
  font-family: sans-serif;
  background-color: #eef2f7;
  color: #1f2937;
  margin: 0;
  padding: 16px;
}

h1 { font-size: 20px; margin: 0 0 12px; }

/* Pemberitahuan masuk: satu putaran, keadaan akhirnya bertahan */
.pemberitahuan {
  display: inline-block;
  margin: 0;
  padding: 12px 18px;
  background-color: #ffffff;
  border: 1px solid #cbd5e1;
  border-left: 4px solid #16a34a;
  border-radius: 8px;
  font-size: 14px;
  animation: muncul 400ms ease-out both;
}

/* Indikator kecil: titik yang berdenyut tanpa henti */
.indikator {
  margin: 16px 0 0;
  font-size: 13px;
  color: #475569;
}

.titik {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 6px;
  border-radius: 50%;
  background-color: #2563eb;
  vertical-align: middle;
  animation: berdenyut 1200ms ease-in-out infinite;
}

@keyframes muncul {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes berdenyut {
  0%   { opacity: 1;    transform: scale(1); }
  50%  { opacity: 0.35; transform: scale(0.8); }
  100% { opacity: 1;    transform: scale(1); }
}

</style></body>
</html>

Pemberitahuan digerakkan dengan kombinasi opacity dan translateY, sehingga kemunculannya halus tanpa memicu perhitungan ulang layout. Perulangan tak terbatas diterapkan hanya pada permukaan yang kecil, seperti titik-titik pemuatan pada demo ini, karena perulangan tanpa henti yang berjalan terus membebani daya pada perangkat bergerak. Apabila prosesnya selesai, animasi indikator dihentikan dengan menghapus class yang membawa properti animation.

Kesalahan umum#

Kesalahan pertama adalah nama keyframe yang tidak cocok dengan nilai animation-name. Browser tidak menampilkan pesan error apa pun untuk ketidakcocokan ini, sehingga elemen tampak normal tetapi tidak beranimasi sama sekali. Perbaikannya, cocokkan nama pada @keyframes dengan nilai yang dirujuk, termasuk besar kecil hurufnya.

Kesalahan kedua adalah animasi yang melompat kembali ke keadaan awal sesudah selesai. Penyebabnya adalah mode pengisian yang tidak disetel, sehingga gaya keyframe terakhir tidak bertahan. Perbaikannya, gunakan animation-fill-mode: forwards atau both, atau pindahkan keadaan akhir ke gaya biasa elemen tersebut.

Kesalahan ketiga adalah menyematkan perulangan infinite pada elemen yang luas. Animasi yang terus berjalan pada area besar membebani pemrosesan dan daya, dan perhatian pengguna juga ikut terbagi. Perbaikannya, batasi perulangan pada indikator kecil, tetapkan jumlah putaran yang wajar, dan hentikan animasinya ketika keadaan yang ditunggu sudah selesai.

Ringkasan#

  • @keyframes mendefinisikan keadaan animasi dengan persen atau kata kunci from dan to, dan browser menginterpolasi properti yang berubah di antara keyframe-nya.
  • Properti animation menjalankan keyframe melalui nama, durasi, kurva, jumlah putaran, arah, dan mode pengisian, dengan durasi ditulis sebelum delay pada shorthand.
  • animation-fill-mode: forwards atau both mempertahankan keadaan akhir, sedangkan tanpa mode pengisian elemen kembali ke gaya biasanya.
  • Animasi berjalan sendiri dan dapat berulang sehingga cocok untuk gerak mandiri, sedangkan transisi menunggu perubahan keadaan.

Referensi#