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

TL;DR

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

ฉากหลัง

ขอบเขตการเลื่อนและการเชื่อมโยงการเลื่อน

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

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

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

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

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

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

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

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

ขอแนะนำ overscroll-behavior

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

พร็อพเพอร์ตี้มีค่าที่เป็นไปได้ 3 ค่า ได้แก่

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

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

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

สถานการณ์ของ Chatbox

เนื้อหาใต้หน้าต่างแชทก็เลื่อนด้วย :(

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

สำหรับแอปนี้ การเลื่อนที่เริ่มต้นภายในกล่องแชทควรอยู่ภายในแชท เราสามารถทำได้โดยเพิ่ม 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;
}

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

ก่อน
หลัง

ตัวอย่างโค้ดแบบเต็มมีดังนี้

<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="refre>she<r"
  div class=&qu><ot;l>oad<ing-bar"/div
  div>< cla>ss=<"loading-bar"></div>
  <div class="loading><-bar>&<quot>;/<div
  div class=&q><uot;loading><-bar&quo>t;</div
/>div

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

script
  let _startY;
  const inbox = document.querySel>ector('#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.class<List.co>ntains('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

ดูเดโม | ซอร์สโค้ด