Empat fitur CSS baru untuk animasi masuk dan keluar yang lancar

Gerakan adalah bagian inti dari setiap pengalaman digital, yang memandu pengguna dari satu interaksi ke interaksi berikutnya. Tetapi ada beberapa celah dalam animasi yang halus di platform web. Ini termasuk kemampuan untuk menganimasikan animasi masuk dan keluar dengan mudah, serta menganimasikan ke dan dari lapisan atas dengan lancar untuk elemen yang dapat ditutup seperti dialog dan popover.

Untuk mengisi kekurangan ini, Chrome 116 dan 117 menyertakan empat fitur platform web baru, yang memungkinkan transisi dan animasi yang mulus untuk properti yang terpisah.

Keempat fitur baru ini mencakup:

  • Kemampuan untuk menganimasikan display dan content-visibility pada linimasa keyframe (Dari Chrome 116).
  • Properti transition-behavior dengan kata kunci allow-discrete untuk mengaktifkan transisi properti terpisah seperti display (Dari Chrome 117).
  • Aturan @starting-style untuk menganimasikan efek entri dari display: none dan ke lapisan atas (Dari Chrome 117).
  • Properti overlay untuk mengontrol perilaku lapisan atas selama animasi (Dari Chrome 117).

Menampilkan animasi dalam keyframe

Mulai Chrome 116, Anda dapat menggunakan display dan content-visibility dalam aturan keyframe. Keduanya kemudian akan ditukar saat keyframe terjadi. Tidak ada nilai baru tambahan yang diperlukan untuk mendukung hal ini:

.card {
  animation: fade-out 0.5s forwards;
}

@keyframes fade-out {
  100% {
    opacity: 0;
    display: none;
  }
}

Contoh sebelumnya menganimasikan opasitas ke 0 selama durasi 0,5 detik, lalu menyetel tampilan ke tidak ada. Selain itu, kata kunci forwards memastikan bahwa animasi tetap berada di status akhir, sehingga elemen yang diterapkan tetap display: none dan opacity: 0.

Ini adalah contoh sederhana yang meniru tindakan yang dapat Anda lakukan dengan transisi (lihat demo di bagian transisi). Namun, transisi tidak dapat membuat animasi yang lebih kompleks, seperti contoh berikut:

.card {
  animation: spin-and-delete 1s ease-in forwards;
}

@keyframes spin-and-delete {
  0% {
    transform: rotateY(0);
    filter: hue-rotate(0);
  }
  80% {
    transform: rotateY(360deg);
    filter: hue-rotate(180deg);
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
  }
}

Animasi spin-and-delete adalah animasi keluar. Pertama, kartu akan berputar pada sumbu y, dijalankan melalui rotasi hue, lalu pada 80% melalui linimasa, mengubah opasitasnya dari 1 menjadi 0. Terakhir, kartu akan ditukar dari display: block menjadi display: none.

Untuk animasi keluar ini, daripada menerapkannya langsung ke elemen, Anda dapat menyiapkan pemicu untuk animasi tersebut. Misalnya dengan melampirkan pemroses peristiwa ke tombol yang memicu class untuk menerapkan animasi, seperti ini:

.spin-out {
   animation: spin-and-delete 1s ease-in forwards;
}
document.querySelector('.delete-btn').addEventListener('click', () => {
 document.querySelector('.card').classList.add('spin-out');
})

Contoh di atas sekarang memiliki status akhir display:none. Ada banyak kasus di mana Anda ingin melangkah lebih jauh dan menghapus node DOM dengan waktu tunggu agar animasi dapat diselesaikan terlebih dahulu.

Mentransisikan animasi diskret

Tidak seperti saat menganimasikan properti terpisah dengan keyframe, untuk mentransisikan properti terpisah, Anda harus menggunakan mode perilaku transisi allow-discrete.

Properti transition-behavior

Mode allow-discrete adalah faktor yang memungkinkan transisi terpisah, dan merupakan nilai properti transition-behavior. transition-behavior menerima dua nilai: normal dan allow-discrete.

.card {
  transition: opacity 0.25s, display 0.25s;
  transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}

.card.fade-out {
  opacity: 0;
  display: none;
}
Catatan: Demo transisi ini menunjukkan teknik yang berbeda dengan demo animasi pertama, tetapi terlihat mirip secara visual.

Singkatan transition juga menetapkan nilai ini, sehingga Anda dapat menghilangkan properti dan menggunakan kata kunci allow-discrete di akhir singkatan transition untuk setiap transisi.

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

Jika Anda menganimasikan beberapa properti terpisah, Anda harus menyertakan allow-discrete setelah setiap properti yang ingin dianimasikan. Contoh:

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

Aturan @starting-style untuk animasi entri

Sejauh ini, artikel ini telah membahas animasi keluar. Untuk membuat animasi entri, Anda harus menggunakan aturan @starting-style.

Gunakan @starting-style untuk menerapkan gaya yang dapat dicari browser sebelum elemen dibuka di halaman. Ini adalah status "sebelum dibuka" (tempat Anda membuat animasi darinya).

/*  0. BEFORE-OPEN STATE   */
/*  Starting point for the transition */
@starting-style {
  .item {
    opacity: 0;
    height: 0;
  }
}

/*  1. IS-OPEN STATE   */
/*  The state at which the element is open + transition logic */
.item {
  height: 3rem;
  display: grid;
  overflow: hidden;
  transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete;
}

/*  2. EXITING STATE   */
/*  While it is deleting, before DOM removal in JS, apply this
    transformation for height, opacity, and a transform which
    skews the element and moves it to the left before setting
    it to display: none */
.is-deleting {
  opacity: 0;
  height: 0;
  display: none;
  transform: skewX(50deg) translateX(-25vw);
}

Sekarang Anda memiliki status masuk dan keluar untuk item daftar TODO ini:

Menganimasikan elemen ke dan dari lapisan atas

Untuk menganimasikan elemen ke dan dari lapisan atas, tentukan @starting-style pada status "terbuka" untuk memberi tahu browser tempat animasi berasal. Untuk dialog, status terbuka ditentukan dengan atribut [open]. Untuk popover, gunakan class semu :popover-open.

Contoh dialog sederhana dapat terlihat seperti ini:

/*   0. BEFORE-OPEN STATE   */
@starting-style {
  dialog[open] {
    translate: 0 100vh;
  }
}

/*   1. IS-OPEN STATE   */
dialog[open] {
  translate: 0 0;
}

/*   2. EXIT STATE   */
dialog {
  transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete;
  translate: 0 100vh;
}

Pada contoh berikutnya, efek masuk dan keluar akan berbeda. Masuk dengan menganimasikan ke atas dari bagian bawah area pandang, keluar dari efek ke bagian atas area pandang. Kode ini juga ditulis dengan CSS bertingkat untuk enkapsulasi visual yang lebih banyak.

Saat menganimasikan popover, gunakan class semu :popover-open, bukan atribut open yang digunakan sebelumnya.

.settings-popover {
  &:popover-open {
    /*  0. BEFORE-OPEN STATE  */
    /*  Initial state for what we're animating *in* from, 
        in this case: goes from lower (y + 20px) to center  */
    @starting-style {
      transform: translateY(20px);
      opacity: 0;
    }
    
    /*  1. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;
  }
  
  /*  2. EXIT STATE  */
  /*  Initial state for what we're animating *out* to , 
      in this case: goes from center to (y - 50px) higher */
  transform: translateY(-50px);
  opacity: 0;
  
  /*  Enumerate transitioning properties, 
      including display and allow-discrete mode */
  transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete;
}

Properti overlay

Terakhir, untuk memudarkan popover atau dialog dari lapisan atas, tambahkan properti overlay ke daftar transisi Anda. popover dan dialog meng-escape klip dan transformasi ancestor, serta menempatkan konten di lapisan atas. Jika Anda tidak melakukan transisi overlay, elemen Anda akan segera kembali terpotong, diubah, dan ditutupi, dan Anda tidak akan melihat transisi terjadi.

[open] {
  transition: opacity 1s, display 1s allow-discrete;
}

Sebagai gantinya, sertakan overlay dalam transisi atau animasi untuk menganimasikan overlay bersama dengan fitur lainnya dan pastikan elemen tetap berada di lapisan atas saat menganimasikan. Proses ini akan terlihat jauh lebih lancar.

[open] {
  transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete;
}

Selain itu, bila Anda memiliki beberapa elemen yang terbuka di lapisan atas, overlay membantu Anda mengontrol transisi yang mulus ke dalam dan ke luar lapisan teratas. Anda dapat melihat perbedaannya dalam contoh sederhana ini. Jika Anda tidak menerapkan overlay ke pop-up kedua saat mentransisikannya, aplikasi akan keluar dari lapisan atas terlebih dahulu, melompat di belakang popover lainnya, sebelum memulai transisi. Efek ini tidak terlalu mulus.

Catatan tentang transisi tampilan

Jika Anda melakukan perubahan DOM, seperti menambahkan dan menghapus elemen dari DOM, solusi bagus lainnya untuk animasi yang mulus adalah transisi tampilan. Berikut adalah dua contoh di atas yang dibuat menggunakan transisi tampilan.

Dalam demo pertama ini, alih-alih menyiapkan @starting-style dan transformasi CSS lainnya, transisi tampilan akan menangani transisi. Transisi tampilan disiapkan seperti berikut:

Pertama, di CSS, berikan view-transition-name individual untuk setiap kartu.

.card-1 {
  view-transition-name: card-1;
}

.card-2 {
  view-transition-name: card-2;
}

/* etc. */

Kemudian, di JavaScript, gabungkan mutasi DOM (dalam hal ini, menghapus kartu), dalam transisi tampilan.

deleteBtn.addEventListener('click', () => {
  // Check for browser support
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      // DOM mutation
      card.remove();
    });
  } 
  // Alternative if no browser support
  else {
    card.remove();
  }
})

Sekarang, browser dapat menangani fade out dan morph setiap kartu ke posisi barunya.

Contoh lain kegunaan ini adalah dengan demo tambahkan/hapus item daftar. Dalam hal ini, Anda harus ingat untuk menambahkan view-transition-name unik untuk setiap kartu yang dibuat.

Kesimpulan

Fitur platform baru ini membawa kita selangkah lebih dekat untuk memperlancar animasi masuk dan keluar di platform web. Untuk mempelajari lebih lanjut, lihat tautan berikut: