Membuat Indikator Progres Scroll

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:

  1. Elemen yang berfungsi sebagai status progres. Elemen ini biasanya berupa position: fixed atau position: absolute agar tetap terlihat saat pengguna men-scroll.
  2. 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 @keyframes yang menskalakan status progres. Pendekatan yang umum adalah menskalakan elemen dari scaleX(0) ke scaleX(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-polyfill untuk strategi penggantian karena fiturnya tidak lengkap dan memiliki banyak masalah umum.
    • Jika animasi hanya dianggap sebagai dekoratif, pilih Progressive Enhancement dan JANGAN berikan penggantian.
  • 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-motion untuk 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 transform dan opacity. Menganimasikan properti lain seperti width atau height dapat menyebabkan masalah performa.
  • LAKUKAN penggunaan urutan deklarasi yang benar: Saat menggunakan properti singkat animation, deklarasikan animation-timeline setelahnya 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) atau scroll(self).
    • Jika root, nearest, atau self tidak cukup, gunakan scroll-timeline bernama.
  • OPSIONAL jelaskan sumbu yang akan dilacak: Jika tidak menargetkan sumbu block default (seperti dalam scroller horizontal), jelaskan sumbu yang akan dilacak dengan scroll(block) atau scroll(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 block default (seperti dalam scroller horizontal), tentukan sumbu yang akan dilacak dengan scroll-timeline-axis.
  • LAKUKAN: pastikan cakupan pencarian berfungsi: Jika elemen yang mendeklarasikan scroll-timeline bukan ancestor hierarki datar dari elemen animasi, naikkan visibilitas nama scroll-timeline menggunakan timeline-scope pada 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>