- Gunakan
clip-pathdanmask-imageuntuk pengungkapan geometris kustom dan fade-out yang halus. - Gunakan Scroll-Driven Animations (
animation-timeline: scroll()) untuk efek yang terikat scroll yang tidak penting, bukan pemroses JS. - Gunakan Transisi Tampilan untuk menganimasikan transisi antar-status tata letak yang kompleks dengan lancar.
Performa
- Sebaiknya animasikan
opacitydantransform(termasuk properti transformasi individual, misalnyatranslate, bukanleft/right/top/bottom) untuk memastikan animasi tetap berada di thread compositor. - Gunakan
transition-behavior: allow-discrete,@starting-style, dan (untuk elemen lapisan atas seperti<dialog>atau[popover])overlayuntuk menganimasikan status masuk dan keluar diskret secara native; lihat Menganimasikan Masuk dan Keluar Elemen dan Menganimasikan Elemen Ke dan Dari Lapisan Atas.
.popover-reveal {
/* Transition discrete display and top-layer overlay alongside opacity */
transition:
opacity ease-out,
display,
overlay;
transition-duration: 0.2s;
transition-behavior: allow-discrete;
}
Aksesibilitas
Gunakan kueri media prefers-reduced-motion untuk menonaktifkan gerakan berat bagi pengguna yang lebih menyukainya.
- Menyediakan mekanisme Jeda: Memungkinkan pengguna menghentikan carousel, banner, atau animasi persisten lainnya yang berjalan otomatis.
- Default ke tampilan statis: Pertimbangkan untuk menetapkan status statis sebagai default dan mengizinkan pengguna memilih untuk mengaktifkan gerakan.
- Jangan melebihi batas kedipan (tiga per detik): Jangan pernah menyertakan kedipan cepat dari terang ke gelap. Efek tersebut dapat menyebabkan kejang.
JANGAN menerapkan animation-duration: 0.01ms; secara global karena dapat menyebabkan animasi tertentu menjadi lebih mengganggu.
Sebagai gantinya, tangani preferensi gerakan berdasarkan kasus per kasus menggunakan salah satu dari dua pendekatan, yaitu menyusun kueri @media di dalam aturan sehingga kedua versi tetap bersama:
- Mulai dengan animasi yang diaktifkan secara default, dan kurangi intensitasnya menggunakan
@media (prefers-reduced-motion: reduce):
/* Good: Dampen spin states or slow down animations for reduced motion */
.spinner {
animation: spin 1s linear infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
opacity: 0.5;
}
}
progress:not([value]) {
animation: slide 1s infinite linear;
@media (prefers-reduced-motion: reduce) {
animation-duration: 20s;
}
}
- Mulai tanpa animasi secara default, dan aktifkan menggunakan
@media (prefers-reduced-motion: no-preference):
/* Good: Default to static or gentle states and opt-in to full motion */
.spinner {
opacity: 0.5;
@media (prefers-reduced-motion: no-preference) {
opacity: 1;
animation: spin 1s linear infinite;
}
}
progress:not([value]) {
animation: slide 20s infinite linear;
@media (prefers-reduced-motion: no-preference) {
animation-duration: 1s;
}
}
Menggunakan peristiwa animasi dan transisi di JavaScript
Pada antarmuka AnimationEvent (animationstart, animationiteration, animationend, animationcancel) dan TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel), event.target memberi Anda elemen DOM asal. Untuk mengakses objek Animation tertentu (Animation, CSSAnimation, atau CSSTransition) yang memicu peristiwa, gunakan properti event.animation hanya baca.
Jika event.animation tidak tersedia, Anda biasanya dapat menemukan instance Animation dengan memanggil event.target.getAnimations() dan memfilter menurut event.animationName (atau event.propertyName untuk transisi). Namun, sebaiknya gunakan event.animation secara langsung, untuk menghindari beberapa skenario saat penggantian yang memfilter event.target.getAnimations() ini tidak berfungsi seperti yang diharapkan:
- Animasi Web Animations API (
element.animate()) tidak memilikianimationName, sehingga tidak dapat dicocokkan berdasarkan nama saat memfilterevent.target.getAnimations(). - Pemfilteran menurut
animationNameuntukAnimationEventbersifat ambigu jika beberapa animasi dengan nama yang sama dilampirkan ke elemen yang sama.- Perhatikan bahwa pemfilteran pada
transitionPropertyuntukTransitionEventberfungsi karena hanya ada satu peristiwa per properti transisi.
- Perhatikan bahwa pemfilteran pada
event.targetselalu mengarah ke elemen DOM asal, sehingga tidak membedakan antara animasi yang berjalan pada elemen itu sendiri dan salah satu elemen semunya — sedangkanevent.animation.effect.pseudoElementmengekspos elemen semu target secara langsung.
// Works in most basic CSS animation cases, but fails for WAAPI animations,
// duplicate animation names on the same element, or pseudo-elements.
document.addEventListener('animationstart', (event) => {
const animation = event.target
.getAnimations()
.find((anim) => anim.animationName === event.animationName);
console.log(animation);
});
// PREFERRED: Directly access the exact Animation object via event.animation
document.addEventListener('animationstart', (event) => {
const animation = event.animation;
console.log(animation);
});
Anda dapat mendeteksi fitur dukungan menggunakan 'animation' in AnimationEvent.prototype (atau 'animation' in TransitionEvent.prototype).