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

สรุปคร่าวๆ

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

ที่มา

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

โซ่เลื่อนบน Chrome Android

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

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

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

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

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

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

เราน่าจะดีกว่านี้ได้

ขอแนะนำ overscroll-behavior

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

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

  1. auto - ค่าเริ่มต้น การเลื่อนที่มาจากองค์ประกอบอาจเผยแพร่ไปยังองค์ประกอบระดับบน
  2. contain - ป้องกันการทำเชนการเลื่อน การเลื่อนจะไม่เผยแพร่ไปยังระดับบน แต่จะแสดงเอฟเฟกต์ภายในโหนด เช่น เอฟเฟกต์การเรืองแสงแบบเลื่อนผ่านใน Android หรือเอฟเฟกต์แถบยางใน iOS ซึ่งจะแจ้งเตือนผู้ใช้เมื่อโดนขอบเขตการเลื่อน หมายเหตุ: การใช้ overscroll-behavior: contain ในองค์ประกอบ html จะป้องกันไม่ให้ระบบนำทางแบบเลื่อนผ่าน
  3. ไม่มี - เหมือนกับ 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 ในการสร้างภาพเคลื่อนไหวแบบดึงเพื่อรีเฟรชที่กำหนดเอง และปิดใช้การเลื่อนจากการ Escape วิดเจ็ตช่องแชท การเลือกตัวเลือกนี้ช่วยมอบประสบการณ์การใช้งานที่ดีที่สุดแก่ผู้ใช้ ซึ่งอาจทำได้ยากหากไม่มี CSSoverscroll-behavior

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