สร้างสัญญาณบอกสถานะความคืบหน้าของการเลื่อน

ตัวบ่งชี้ความคืบหน้าในการเลื่อนเป็นรูปแบบอินเทอร์เฟซผู้ใช้ที่พบบ่อยซึ่งสื่อสารความคืบหน้าของผู้ใช้ผ่านเอกสารหรือคอนเทนเนอร์ที่เลื่อนได้ด้วยภาพ เมื่อผู้ใช้เลื่อน องค์ประกอบภาพจะอัปเดตเพื่อแสดงตำแหน่งของผู้ใช้ ซึ่งช่วยให้ผู้ใช้ทราบได้อย่างชัดเจนและเข้าใจง่ายว่าดูเนื้อหาไปแล้วเท่าใดและเหลืออีกเท่าใด

วิธีการนำไปใช้งาน

หากต้องการสร้างตัวบ่งชี้ความคืบหน้าในการเลื่อน คุณต้องมี 2 สิ่งต่อไปนี้

  1. องค์ประกอบที่ทำหน้าที่เป็นแถบความคืบหน้า โดยทั่วไปองค์ประกอบนี้จะเป็น position: fixed หรือ position: absolute เพื่อให้อยู่ในมุมมองขณะที่ผู้ใช้เลื่อนดูเนื้อหา
  2. ภาพเคลื่อนไหวที่ลิงก์กับตำแหน่งการเลื่อน

ซึ่งคุณสามารถทำได้ดังนี้

  • ก่อนอื่น ให้สร้างองค์ประกอบ HTML ที่จะทำหน้าที่เป็นแถบความคืบหน้า คุณจัดสไตล์องค์ประกอบนี้ได้ตามต้องการ
  • ถัดไป ใน CSS ให้กำหนดภาพเคลื่อนไหว @keyframes ที่ปรับขนาดแถบความคืบหน้า แนวทางที่พบบ่อยคือการปรับขนาดองค์ประกอบจาก scaleX(0) เป็น scaleX(1)
  • สุดท้าย ให้ใช้ภาพเคลื่อนไหวนี้กับองค์ประกอบแถบความคืบหน้าและตั้งค่า animation-timeline เป็น scroll-timeline ซึ่งจะบอกให้เบราว์เซอร์ขับเคลื่อนความคืบหน้าของภาพเคลื่อนไหวตามตำแหน่งการเลื่อนของตัวเลื่อนระดับบนสุดที่ใกล้ที่สุด

โค้ดตัวอย่าง

โค้ดนี้จะขยายองค์ประกอบ #progress เมื่อเลื่อนโดยใช้ไทม์ไลน์การเลื่อนแบบไม่ระบุตัวตน ซึ่งสร้างโดยฟังก์ชัน 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();
    }
  }
}

เนื่องจากตำแหน่งใน DOM ฟังก์ชัน scroll() จะติดตามตัวเลื่อนระดับบนสุดที่ใกล้ที่สุดในทิศทาง block ซึ่งในที่นี้คือตัวเลื่อนระดับราก

<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>

โค้ดนี้จะขยายองค์ประกอบ #progress เมื่อเลื่อนโดยใช้ไทม์ไลน์การเลื่อนที่มีชื่อ ซึ่งสร้างโดยพร็อพเพอร์ตี้ 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;
    }
  }
}

แนวทางปฏิบัติแนะนำ

เมื่อใช้ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน คุณควรทำตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ราบรื่นและเข้าถึงได้

  • ควรรวมการตรวจหาฟีเจอร์: เบราว์เซอร์บางรายการไม่รองรับภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน ใช้ @supports (animation-timeline: scroll()) เพื่อตรวจสอบการรองรับและระบุตัวเลือกสำรองสำหรับเบราว์เซอร์ที่ไม่รองรับ
    • อย่าใช้แพ็กเกจ scroll-timeline-polyfill สำหรับกลยุทธ์สำรอง เนื่องจากฟีเจอร์ยังไม่สมบูรณ์และมีปัญหาที่ทราบอยู่แล้วจำนวนมาก
    • หากภาพเคลื่อนไหวถือเป็นเพียงการตกแต่ง ให้เลือกใช้การเพิ่มประสิทธิภาพแบบต่อเนื่อง และอย่าระบุทางเลือกสำรอง
  • ควรนำองค์ประกอบที่ใช้เพื่อการตกแต่งเท่านั้นออกจากขั้นตอนการอ่านของเทคโนโลยีความช่วยเหลือพิเศษ โดยใช้ aria-hidden="true" กับตัวบ่งชี้การเลื่อนที่เป็นภาพเท่านั้น เพื่อให้โปรแกรมอ่านหน้าจอไม่พบโหนดที่ว่างเปล่าและไม่มีชื่อ
  • ควรเคารพค่ากำหนดของผู้ใช้: ผู้ใช้บางรายต้องการให้มีการเคลื่อนไหวน้อยลงบนเว็บ ใช้คิวรีสื่อ prefers-reduced-motion เพื่อปิดใช้หรือลดภาพเคลื่อนไหวสำหรับผู้ใช้เหล่านี้
  • ควรพยายามทำให้เฉพาะพร็อพเพอร์ตี้ CSS ที่มีประสิทธิภาพเคลื่อนไหว: เพื่อให้ภาพเคลื่อนไหวราบรื่นที่สุด ให้ใช้พร็อพเพอร์ตี้ที่เคลื่อนไหวซึ่งจัดการได้โดยเทรด Compositor ของเบราว์เซอร์ เช่น transform และ opacity การทำให้พร็อพเพอร์ตี้อื่นๆ เช่น width หรือ height เคลื่อนไหวอาจทำให้เกิดปัญหาด้านประสิทธิภาพ
  • ควรใช้ลำดับการประกาศที่ถูกต้อง: เมื่อใช้พร็อพเพอร์ตี้ชวเลข animation ให้ประกาศ animation-timeline หลังจากพร็อพเพอร์ตี้ดังกล่าวเพื่อป้องกันไม่ให้ชวเลขรีเซ็ตไทม์ไลน์

เมื่อใช้ฟังก์ชัน scroll() เพื่อสร้างภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน ให้ทำดังนี้

  • ไม่บังคับ ระบุตัวเลื่อนอย่างชัดเจน: เมื่อไม่ได้กำหนดเป้าหมายตัวเลื่อนระดับบนสุดที่ใกล้ที่สุด ให้ระบุตัวเลื่อนที่ต้องการใช้อย่างชัดเจนด้วย scroll(root) หรือ scroll(self)
    • เมื่อ root, nearest หรือ self ไม่เพียงพอ ให้ใช้ scroll-timeline ที่มีชื่อ
  • ไม่บังคับ ระบุแกนที่จะติดตามอย่างชัดเจน: เมื่อไม่ได้กำหนดเป้าหมายแกน block เริ่มต้น (เช่น ในแถบเลื่อนแนวนอน) ให้ระบุแกนที่จะติดตามอย่างชัดเจนด้วย scroll(block) หรือ scroll(inline)

เมื่อใช้พร็อพเพอร์ตี้ scroll-timeline เพื่อสร้างภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน ให้ทำดังนี้

  • ควรใช้ CSS <dashed-ident> สำหรับชื่อ
  • ไม่บังคับ ระบุแกนที่จะติดตามอย่างชัดเจน: เมื่อไม่ได้กำหนดเป้าหมายแกน block เริ่มต้น (เช่น ในแถบเลื่อนแนวนอน) ให้ระบุแกนที่จะติดตามด้วย scroll-timeline-axis อย่างชัดเจน
  • ควรตรวจสอบว่าขอบเขตของการค้นหาใช้งานได้: เมื่อองค์ประกอบที่ประกาศ scroll-timeline ไม่ใช่ระดับบนสุดแบบ Flat Tree ขององค์ประกอบที่เคลื่อนไหว ให้เพิ่มระดับการมองเห็นชื่อของ scroll-timeline โดยใช้ timeline-scope ในระดับบนสุดที่ใช้ร่วมกัน

กลยุทธ์สำรอง

สำหรับเบราว์เซอร์ที่ไม่รองรับภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน คุณสามารถใช้โหมดสำรองเพื่อสร้างเอฟเฟกต์ภาพขึ้นมาใหม่ได้ โดยปกติแล้ว ฟังก์ชันสำรองจะสร้างขึ้นด้วย Listener การเลื่อน (สำหรับเอฟเฟกต์ ScrollTimeline) หรือ IntersectionObserver API (สำหรับเอฟเฟกต์ ViewTimeline)

ในเบราว์เซอร์ที่มีการรองรับภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนในตัว ให้ใช้การติดตั้งใช้งาน CSS แบบเนทีฟเสมอ เนื่องจากมีประสิทธิภาพมากกว่า

โปรดทราบว่าคุณไม่สามารถสร้างเอฟเฟกต์ทั้งหมดขึ้นมาใหม่ได้โดยใช้วิธีการสำรอง

สำหรับ Use Case นี้โดยเฉพาะ สคริปต์ต่อไปนี้จะใช้โหมดสำรองสำหรับเบราว์เซอร์ที่ไม่รองรับภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน โดยจะใช้ Listener การเลื่อนเพื่อติดตามตำแหน่งการเลื่อนขององค์ประกอบรูทและอัปเดตพร็อพเพอร์ตี้ transform ของแถบความคืบหน้าตามลำดับ

<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>