Indikator progres scroll adalah pola antarmuka pengguna umum yang secara visual mengomunikasikan progres pengguna melalui dokumen atau penampung yang dapat di-scroll. Saat pengguna men-scroll, elemen visual akan diperbarui untuk mencerminkan posisinya, sehingga memberikan gambaran yang jelas dan intuitif tentang jumlah konten yang telah dilihat dan jumlah konten yang tersisa.
Cara menerapkan
Untuk membuat indikator progres scroll, Anda memerlukan dua hal:
- Elemen yang berfungsi sebagai status progres. Elemen ini biasanya berupa
position: fixedatauposition: absoluteagar tetap terlihat saat pengguna men-scroll. - Animasi yang ditautkan ke posisi scroll.
Berikut cara melakukannya:
- Pertama, buat elemen HTML yang akan berfungsi sebagai status progres. Elemen ini dapat diberi gaya sesuai keinginan Anda.
- Selanjutnya, di CSS Anda, tentukan animasi
@keyframesyang menskalakan status progres. Pendekatan yang umum adalah menskalakan elemen dariscaleX(0)kescaleX(1). - Terakhir, terapkan animasi ini ke elemen status progres dan tetapkan
animation-timeline-nya ke scroll-timeline. Hal ini memberi tahu browser untuk menggerakkan progres animasi berdasarkan posisi scroll dari scroller ancestor terdekat.
Kode contoh
Kode ini memperbesar elemen #progress saat di-scroll menggunakan scroll-timeline anonim, yang dibuat oleh fungsi scroll().
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: scroll();
}
}
}
Karena lokasinya di DOM, fungsi scroll() akan melacak scroller ancestor terdekatnya dalam arah block, yang di sini adalah scroller root.
<body>
<!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
<div id="progress" aria-hidden="true"></div>
</body>
Kode ini memperbesar elemen #progress saat di-scroll menggunakan scroll-timeline bernama, yang dibuat oleh properti scroll-timeline.
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
:root {
scroll-timeline: --tl block;
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: --tl;
}
}
}
Praktik Terbaik
Saat menggunakan animasi berbasis scroll, penting untuk mengikuti beberapa praktik terbaik guna memastikan pengalaman yang lancar dan mudah diakses:
- LAKUKAN penyertaan deteksi fitur: Tidak semua browser mendukung animasi berbasis scroll. Gunakan
@supports (animation-timeline: scroll())untuk memeriksa dukungan dan menyediakan penggantian untuk browser yang tidak mendukungnya.- JANGAN gunakan paket
scroll-timeline-polyfilluntuk strategi penggantian karena fiturnya tidak lengkap dan memiliki banyak masalah umum. - Jika animasi hanya dianggap sebagai dekoratif, pilih Progressive Enhancement dan JANGAN berikan penggantian.
- JANGAN gunakan paket
- LAKUKAN penghapusan elemen yang murni dekoratif dari alur pembacaan teknologi pendukung: Terapkan
aria-hidden="true"ke indikator scroll yang murni visual untuk memastikan pembaca layar tidak menemukan node kosong tanpa nama. - LAKUKAN penghormatan terhadap preferensi pengguna: Beberapa pengguna lebih memilih untuk memiliki lebih sedikit gerakan di web. Gunakan kueri media
prefers-reduced-motionuntuk menonaktifkan atau mengurangi animasi bagi pengguna ini. - LAKUKAN upaya untuk menganimasikan hanya properti CSS yang berperforma tinggi: Untuk animasi yang paling lancar, tetap gunakan properti animasi yang dapat ditangani oleh thread compositor browser, seperti
transformdanopacity. Menganimasikan properti lain sepertiwidthatauheightdapat menyebabkan masalah performa. - LAKUKAN penggunaan urutan deklarasi yang benar: Saat menggunakan properti singkat
animation, deklarasikananimation-timelinesetelahnya untuk mencegah properti singkat mereset linimasa.
Saat menggunakan fungsi scroll() untuk membuat animasi berbasis scroll:
- OPSIONAL tentukan scroller secara eksplisit: Jika tidak menargetkan scroller ancestor terdekat, tentukan scroller yang ingin Anda gunakan secara eksplisit dengan
scroll(root)atauscroll(self).- Jika
root,nearest, atauselftidak cukup, gunakan scroll-timeline bernama.
- Jika
- OPSIONAL jelaskan sumbu yang akan dilacak: Jika tidak menargetkan sumbu
blockdefault (seperti dalam scroller horizontal), jelaskan sumbu yang akan dilacak denganscroll(block)atauscroll(inline).
Saat menggunakan properti scroll-timeline untuk membuat animasi berbasis scroll:
- LAKUKAN penggunaan
<dashed-ident>CSS untuk nama. - OPSIONAL tentukan sumbu yang akan dilacak: Jika tidak menargetkan sumbu
blockdefault (seperti dalam scroller horizontal), tentukan sumbu yang akan dilacak denganscroll-timeline-axis. - LAKUKAN: pastikan cakupan pencarian berfungsi: Jika elemen yang mendeklarasikan
scroll-timelinebukan ancestor hierarki datar dari elemen animasi, naikkan visibilitas namascroll-timelinemenggunakantimeline-scopepada ancestor bersama.
Strategi penggantian
Untuk browser yang tidak mendukung animasi berbasis scroll, Anda dapat menggunakan penggantian untuk membuat ulang efek visual. Penggantian biasanya dibuat dengan pemroses scroll (untuk efek ScrollTimeline) atau IntersectionObserver API (untuk efek ViewTimeline).
Di browser dengan dukungan bawaan untuk animasi berbasis scroll, SELALU gunakan implementasi CSS native karena lebih berperforma.
Perhatikan bahwa tidak semua efek dapat dibuat ulang menggunakan pendekatan penggantian.
Khusus untuk kasus penggunaan ini, skrip berikut menerapkan penggantian untuk browser yang tidak mendukung animasi berbasis scroll. Elemen ini menggunakan pemroses scroll untuk melacak posisi scroll elemen root dan memperbarui properti transform status progres.
<script>
if (!CSS.supports('animation-timeline', 'scroll()')) {
const progress = document.querySelector('#progress');
window.addEventListener('scroll', () => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progressPercentage = (scrolled / scrollable);
progress.style.transform = `scaleX(${progressPercentage})`;
});
}
</script>