ควบคุมการเลื่อนของคุณ - ปรับแต่งเอฟเฟกต์การดึงเพื่อรีเฟรชและเอฟเฟกต์เพิ่มเติม

TL;DR

พร็อพเพอร์ตี้ CSS overscroll-behavior ช่วยให้นักพัฒนาแอปสามารถลบล้างลักษณะการเลื่อนแบบแสดงผลเกินขอบเขตเริ่มต้นของเบราว์เซอร์เมื่อถึงด้านบน/ด้านล่างของเนื้อหา กรณีการใช้งาน ได้แก่ การปิดใช้ฟีเจอร์การดึงเพื่อรีเฟรชในอุปกรณ์เคลื่อนที่ การนําเอฟเฟกต์เรืองแสงเมื่อเลื่อนผ่านและเอฟเฟกต์การยืดออกเมื่อเลื่อนผ่านออก และการป้องกันไม่ให้เนื้อหาของหน้าเลื่อนเมื่ออยู่ใต้โมดอล/การวางซ้อน

ข้อมูลเบื้องต้น

ขอบเขตการเลื่อนและโซ่การเลื่อน

การเชื่อมโยงการเลื่อนใน Chrome บน Android

การเลื่อนเป็นหนึ่งในวิธีพื้นฐานที่สุดในการโต้ตอบกับหน้าเว็บ แต่รูปแบบ UX บางรูปแบบอาจจัดการได้ยากเนื่องจากลักษณะการทํางานเริ่มต้นของเบราว์เซอร์ที่แปลกๆ ตัวอย่างเช่น ลองดูที่ลิ้นชักแอปซึ่งมีรายการจํานวนมากที่ผู้ใช้อาจต้องเลื่อนดู เมื่อถึงด้านล่างแล้ว คอนเทนเนอร์รายการเพิ่มเติมจะหยุดเลื่อนเนื่องจากไม่มีเนื้อหาให้แสดงอีก กล่าวคือ ผู้ใช้ไปถึง "ขอบเขตการเลื่อน" แต่ให้สังเกตสิ่งที่จะเกิดขึ้นหากผู้ใช้เลื่อนต่อไป เนื้อหาด้านหลังลิ้นชักจะเริ่มเลื่อน คอนเทนเนอร์หลัก (หน้าหลักในตัวอย่างนี้) จะเป็นผู้ควบคุมการเลื่อน

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

เอฟเฟกต์การดึงเพื่อรีเฟรช

การปัดเพื่อรีเฟรชเป็นท่าทางสัมผัสที่ใช้งานง่ายซึ่งแอปบนอุปกรณ์เคลื่อนที่อย่าง Facebook และ Twitter นำมาใช้ การดึงฟีดโซเชียลลงแล้วปล่อยจะเป็นการเปิดพื้นที่ใหม่ให้โหลดโพสต์ล่าสุด อันที่จริง UX นี้เริ่มได้รับความนิยมอย่างมากจนเบราว์เซอร์ในอุปกรณ์เคลื่อนที่อย่าง Chrome ใน Android ก็ใช้ผลลัพธ์เดียวกันนี้ การปัดลงที่ด้านบนของหน้าจะรีเฟรชทั้งหน้า

การปัดเพื่อรีเฟรชที่ปรับแต่งเองของ Twitter
เมื่อรีเฟรชฟีดใน PWA
การดึงเพื่อรีเฟรชของ Chrome Android ดั้งเดิม
จะรีเฟรชทั้งหน้า

สำหรับสถานการณ์อย่างเช่น PWA ของ Twitter ก็อาจควรปิดใช้การดึงเพื่อรีเฟรชในตัว เหตุผล ในแอปนี้ คุณอาจไม่ต้องการให้ผู้ใช้รีเฟรชหน้าเว็บโดยไม่ตั้งใจ นอกจากนี้ คุณอาจเห็นภาพเคลื่อนไหวรีเฟรช 2 ครั้งด้วย หรืออาจปรับแต่งการทํางานของเบราว์เซอร์ให้สอดคล้องกับการสร้างแบรนด์ของเว็บไซต์มากขึ้น แต่ข้อเสียคือการปรับแต่งประเภทนี้ทำได้ยาก นักพัฒนาซอฟต์แวร์จึงต้องเขียน JavaScript ที่ไม่จำเป็น เพิ่ม non-passive Listeners ที่ตอบสนองต่อการสัมผัส (ซึ่งบล็อกการเลื่อน) หรือติดทั้งหน้าใน 100vw/vh<div> (เพื่อป้องกันไม่ให้หน้าเว็บแสดงเกินขอบ) การแก้ปัญหาเหล่านี้ส่งผลเสียต่อประสิทธิภาพการเลื่อนอย่างมีเอกสารประกอบ

เราทำได้ดีกว่านี้

ขอแนะนำ overscroll-behavior

overscroll-behavior property เป็นฟีเจอร์ CSS ใหม่ที่ควบคุมลักษณะการทํางานที่เกิดขึ้นเมื่อคุณเลื่อนคอนเทนเนอร์ (รวมถึงหน้าเว็บเอง) เกินขอบ คุณสามารถใช้เพื่อยกเลิกการเชื่อมโยงการเลื่อน ปิดใช้/ปรับแต่งการดึงเพื่อรีเฟรช ปิดใช้เอฟเฟกต์การยืดบน iOS (เมื่อ Safari ใช้ overscroll-behavior) และอื่นๆ ที่สำคัญคือการใช้ overscroll-behavior จะไม่ส่งผลเสียต่อประสิทธิภาพหน้าเว็บเหมือนแฮ็กที่กล่าวถึงในส่วนนําเข้า

พร็อพเพอร์ตี้ใช้ค่าที่เป็นไปได้ 3 ค่าดังนี้

  1. auto - ค่าเริ่มต้น การเลื่อนที่มาจากองค์ประกอบอาจส่งผลต่อองค์ประกอบหลัก
  2. contain - ป้องกันไม่ให้มีการเลื่อนแบบเชน การเลื่อนจะไม่ส่งไปยังบรรพบุรุษ แต่ระบบจะแสดงผลในท้องถิ่นภายในโหนด เช่น ผลของแสงเรืองบนหน้าจอเมื่อเลื่อนผ่านขอบใน Android หรือผลของแถบยางยืดใน iOS ซึ่งจะแจ้งให้ผู้ใช้ทราบเมื่อถึงขอบเขตการเลื่อน หมายเหตุ: การใช้ overscroll-behavior: contain ในองค์ประกอบ html จะป้องกันไม่ให้เกิดการเลื่อนเกินขณะดำเนินการไปยังส่วนต่างๆ
  3. none - เหมือนกับ contain แต่ป้องกันไม่ให้เอฟเฟกต์การเลื่อนเกินเกิดขึ้นภายในโหนดด้วย (เช่น แสงที่สว่างขึ้นเมื่อเลื่อนเกินใน Android หรือการเลื่อนแบบยืดหยุ่นใน iOS)

มาดูตัวอย่างการใช้งาน overscroll-behavior กัน

ป้องกันไม่ให้การเลื่อนออกจากองค์ประกอบตำแหน่งคงที่

สถานการณ์ในช่องแชท

เนื้อหาใต้หน้าต่างแชทจะเลื่อนเช่นกัน :(

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

สำหรับแอปนี้ การเลื่อนที่อยู่ภายในช่องแชทจะอยู่ในแชทจะเหมาะสมกว่า เราทําเช่นนั้นได้โดยการเพิ่ม overscroll-behavior: contain ไปยังองค์ประกอบที่เก็บข้อความแชท

#chat .msgs {
  overflow: auto;
  overscroll-behavior: contain;
  height: 300px;
}

โดยพื้นฐานแล้ว เรากำลังสร้างการแยกที่สมเหตุสมผลระหว่างบริบทการเลื่อนของกล่องแชทกับหน้าหลัก ผลลัพธ์ที่ได้คือหน้าหลักจะยังคงอยู่เมื่อผู้ใช้เลื่อนไปที่ด้านบน/ด้านล่างของประวัติการแชท การเลื่อนที่เริ่มต้นในช่องแชทจะไม่เผยแพร่

สถานการณ์การซ้อนทับหน้าเว็บ

อีกรูปแบบหนึ่งของสถานการณ์ "การเลื่อนผ่าน" คือเมื่อคุณเห็นเนื้อหาเลื่อนอยู่หลังการวางซ้อนตำแหน่งคงที่ โปรดรอรับoverscroll-behavior เบราว์เซอร์พยายามช่วยเหลือ แต่กลับทำให้เว็บไซต์ดูมีข้อบกพร่อง

ตัวอย่าง - โมดัลที่มีและไม่มี overscroll-behavior: contain

ก่อน: เนื้อหาหน้าเว็บเลื่อนอยู่ใต้โฆษณาซ้อนทับ
หลังจาก: เนื้อหาหน้าเว็บไม่เลื่อนอยู่ใต้การวางซ้อน

การปิดใช้การดึงเพื่อรีเฟรช

การปิดการดึงเพื่อรีเฟรชเป็น CSS เพียงบรรทัดเดียว เพียงแค่ป้องกันไม่ให้มีการเชื่อมโยงการเลื่อนในองค์ประกอบที่กําหนดวิวพอร์ตทั้งหมด ในกรณีส่วนใหญ่ <html> หรือ <body> จะเป็นค่าต่อไปนี้

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

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

ก่อน
หลัง

ต่อไปนี้คือข้อมูลโค้ด แบบเต็ม

<style>
  body.refreshing #inbox {
    filter: blur(1px);
    touch-action: none; /* prevent scrolling */
  }
  body.refreshing .refresher {
    transform: translate3d(0,150%,0) scale(1);
    z-index: 1;
  }
  .refresher {
    --refresh-width: 55px;
    pointer-events: none;
    width: var(--refresh-width);
    height: var(--refresh-width);
    border-radius: 50%;
    position: absolute;
    transition: all 300ms cubic-bezier(0,0,0.2,1);
    will-change: transform, opacity;
    ...
  }
</style>

<div class="refresher">
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
</div>

<section id="inbox"><!-- msgs --></section>

<script>
  let _startY;
  const inbox = document.querySelector('#inbox');

  inbox.addEventListener('touchstart', e => {
    _startY = e.touches[0].pageY;
  }, {passive: true});

  inbox.addEventListener('touchmove', e => {
    const y = e.touches[0].pageY;
    // Activate custom pull-to-refresh effects when at the top of the container
    // and user is scrolling up.
    if (document.scrollingElement.scrollTop === 0 && y > _startY &&
        !document.body.classList.contains('refreshing')) {
      // refresh inbox.
    }
  }, {passive: true});
</script>

การปิดใช้เอฟเฟกต์เรืองแสงเมื่อเลื่อนผ่านขอบและเอฟเฟกต์การเลื่อนแบบยืด

หากต้องการปิดใช้เอฟเฟกต์การเด้งเมื่อถึงขอบเขตการเลื่อน ให้ใช้ตัวเลือกต่อไปนี้ overscroll-behavior-y: none

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
ก่อน: การเลื่อนถึงขอบเขตจะแสดงการเรืองแสง
หลังจาก: ปิดใช้การเรืองแสง

การสาธิตแบบเต็ม

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

ดูการสาธิต | แหล่งที่มา