การเลื่อนที่ควบคุมอย่างดีด้วย CSS Scroll Snap

สร้างประสบการณ์การเลื่อนที่มีการควบคุมอย่างดีด้วยการประกาศตำแหน่งการสแนปการเลื่อน

ฟีเจอร์ CSS Scroll Snap ช่วยให้นักพัฒนาเว็บสร้างประสบการณ์การเลื่อนที่ควบคุมได้ดีด้วยการประกาศตำแหน่งการสแนปการเลื่อน ตัวอย่างที่ใช้กันโดยทั่วไป 2 ตัวอย่างคือบทความที่มีการแบ่งหน้าและภาพสไลด์ CSS Scroll Snap มี API ที่ใช้งานง่ายและสอดคล้องกันสำหรับสร้างรูปแบบ UX ยอดนิยมเหล่านี้

ฉากหลัง

กรณีสำหรับการสแนปการเลื่อน

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

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

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

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

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

การสแนปการเลื่อน CSS

การสแนปการเลื่อนคือการปรับออฟเซ็ตการเลื่อนของคอนเทนเนอร์การเลื่อนให้อยู่ในตำแหน่งสแนปที่ต้องการเมื่อการเลื่อนเสร็จสิ้น

คุณสามารถเลือกใช้คอนเทนเนอร์การเลื่อนในการสแนปการเลื่อนได้โดยใช้พร็อพเพอร์ตี้ scroll-snap-type ซึ่งจะบอกเบราว์เซอร์ว่าควรพิจารณาจัดคอนเทนเนอร์การเลื่อนนี้ให้อยู่ในตำแหน่ง Snap ที่เกิดจากองค์ประกอบสืบทอด scroll-snap-type กำหนดแกนที่เกิดการเลื่อน: x, y หรือ both และความเข้มงวดในการสแนป: mandatory, proximity เราจะพูดถึงเรื่องนี้เพิ่มเติมในภายหลัง

คุณสร้างตำแหน่งสแนปได้โดยประกาศการจัดแนวที่ต้องการในองค์ประกอบ ตำแหน่งนี้คือออฟเซ็ตการเลื่อนที่คอนเทนเนอร์การเลื่อนระดับบนสุดที่ใกล้ที่สุดและองค์ประกอบอยู่ในแนวเดียวกันตามที่ระบุไว้สำหรับแกนที่กำหนด การจัดแนวต่อไปนี้เป็นไปได้ในแต่ละแกน: start, end, center

การจัดแนว start หมายความว่าขอบเริ่มต้นของ Snapport ของคอนเทนเนอร์การเลื่อนควรอยู่ชิดกับขอบเริ่มต้นของพื้นที่สแนปขององค์ประกอบ ในทำนองเดียวกัน การจัดแนว end และ center หมายความว่าขอบหรือจุดศูนย์กลางของจุดสแนปของคอนเทนเนอร์การเลื่อนควรอยู่ในแนวเดียวกับขอบหรือจุดศูนย์กลางของพื้นที่สแนปขององค์ประกอบ

ตัวอย่างการจัดแนวต่างๆ บนแกนการเลื่อนแนวนอน

ตัวอย่างต่อไปนี้จะแสดงวิธีใช้แนวคิดเหล่านี้

กรณีการใช้งานทั่วไปสำหรับการสแนปการเลื่อนคือภาพสไลด์ ตัวอย่างเช่น หากต้องการสร้างภาพสไลด์แนวนอนที่จัดชิดรูปภาพแต่ละรูปเมื่อเลื่อน เราสามารถระบุคอนเทนเนอร์การเลื่อนให้มี scroll-snap-type ที่จำเป็นในแกนนอน ตั้งค่ารูปภาพแต่ละรูปเป็น scroll-snap-align: center เพื่อให้การจัดชิดจัดรูปภาพให้อยู่กึ่งกลางภายในภาพสไลด์

#gallery {
  scroll-snap-type: x mandatory;
  overflow-x: scroll;
  display: flex;
}

#gallery img {
   scroll-snap-align: center;
}
<div id="gallery">
  <img src="cat.jpg">
  <img src="dog.jpg">
  <img src="another_cute_animal.jpg">
</div>

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

ตัวอย่าง: หน้าสินค้าที่ผ่านการปรับปรุง

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

ซึ่งทำได้ดังนี้

article {
  scroll-snap-type: y proximity;
  /* Reserve space for header plus some extra space for sneak peeking. */
  scroll-padding-top: 15vh;
  overflow-y: scroll;
}
section {
  /* Snap align start. */
  scroll-snap-align: start;
}
header {
  position: fixed;
  height: 10vh;
}
<article>
  <header> Header </header>
  <section> Section One </section>
  <section> Section Two </section>
  <section> Section Three </section>
</article>

ระยะห่างจากขอบและระยะขอบของ Scroll

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

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

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

คุณอาจสังเกตเห็นว่าพร็อพเพอร์ตี้ทั้ง 2 รายการนี้ไม่มีคำว่า "snap" อยู่ในชื่อ ซึ่งเป็นความตั้งใจเนื่องจากพร็อพเพอร์ตี้เหล่านี้จะแก้ไขกล่องสำหรับการดำเนินการเลื่อนที่เกี่ยวข้องทั้งหมด ไม่ใช่แค่การสแนปการเลื่อน ตัวอย่างเช่น Chrome จะพิจารณาองค์ประกอบเหล่านี้เมื่อคำนวณขนาดหน้าสำหรับการเลื่อนหน้า เช่น PageDown และ PageUp รวมถึงเมื่อคำนวณจำนวนการเลื่อนสำหรับการดำเนินการ Element.scrollIntoView()

การโต้ตอบกับ API การเลื่อนอื่นๆ

DOM Scrolling API

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

การเลื่อนอย่างราบรื่น

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

ลักษณะการเลื่อนเกิน

Overscroll behavior API ควบคุมวิธีเชื่อมโยงการเลื่อนในองค์ประกอบหลายรายการ และไม่ได้รับผลกระทบจากการสแนปการเลื่อน

ข้อควรระวังและแนวทางปฏิบัติแนะนำ

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

ในหลายกรณี คุณสามารถเพิ่มการสแนปการเลื่อนเป็นการเพิ่มประสิทธิภาพได้โดยไม่ต้องตรวจหาฟีเจอร์ หากจำเป็น ให้ใช้ @supports หรือ CSS.supports เพื่อตรวจหาการรองรับการสแนปการเลื่อน CSS หลีกเลี่ยงการใช้ scroll-snap-type ซึ่งมีอยู่ในข้อกำหนดที่เลิกใช้งานแล้วด้วย

การตรวจหาฟีเจอร์ใน CSS

@supports (scroll-snap-align: start) {
  article {
    scroll-snap-type: y proximity;
    scroll-padding-top: 15vh;
    overflow-y: scroll;
  }
}

การตรวจหาฟีเจอร์ใน JavaScript

if (CSS.supports('scroll-snap-align: start')) {
  // use css scroll snap
} else {
  // use fallback
}

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

งานในอนาคต

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

  1. ความพร้อมใช้งานและความเข้ากันได้ของ API ในเบราว์เซอร์ต่างๆ
  2. ทำงานกับ CSS API ใหม่ เช่น scroll-start
  3. ทำงานกับเหตุการณ์ JS ใหม่ เช่น snapChanged()