Kodokon kodokon.com

การโต้ตอบสมัยใหม่: scroll-snap, view transitions

จัดการการเลื่อนแบบแม่เหล็ก การเปลี่ยนสถานะที่เบราว์เซอร์ทำแอนิเมชันให้ และการเคารพ prefers-reduced-motion อย่างเคร่งครัด

11 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

scroll snap เกี่ยวข้องกับผู้เล่นสองฝ่าย: คอนเทนเนอร์เป็นตัวกำหนด snapport (พื้นที่ที่มองเห็นได้ ซึ่งถูกหดด้วย scroll-padding) และกลยุทธ์ผ่าน scroll-snap-type โดย proximity จะสแนปเฉพาะเมื่อใกล้จุดสแนปเท่านั้น ในขณะที่ mandatory รับประกันว่าคุณจะไปหยุดที่จุดสแนปเสมอ แม้กระทั่งหลังจากการเลื่อนด้วยโปรแกรม ส่วนลูก ๆ เป็นตัวประกาศพื้นที่การจัดแนวของมันด้วย scroll-snap-align (ปรับได้ผ่าน scroll-margin) มีรายละเอียดปลีกย่อยที่ไม่ค่อยมีใครรู้: ระหว่างการปัดเร็ว ๆ เบราว์เซอร์อาจ ข้ามจุดสแนปหลายจุด ได้อย่างชอบธรรม scroll-snap-stop: always จะบังคับให้มันหยุดที่แต่ละจุด ซึ่งจำเป็นสำหรับคารูเซลแบบสตอรี

CSS
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1rem;
}
.carousel > article {
  flex: 0 0 80%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
คารูเซลแบบแม่เหล็ก: ทุกสไลด์จัดตัวเองให้อยู่กึ่งกลาง และไม่มีการปัดใดข้ามได้

View Transitions API ทำแอนิเมชันการเปลี่ยนสถานะของ DOM ได้โดยไม่ต้องใช้ไลบรารีใด ๆ document.startViewTransition(callback) จะจับ ภาพสแนปช็อตเชิงภาพ ของสถานะปัจจุบัน รัน callback ของคุณ (ซึ่งเปลี่ยนแปลง DOM) แล้วจึงทำแอนิเมชันระหว่างสถานะเก่าและใหม่ เบื้องหลังนั้น เบราว์เซอร์จะสร้างต้นไม้ของ pseudo-element ทับอยู่บนหน้าเว็บ: ::view-transition บรรจุเอลิเมนต์ ::view-transition-group ซึ่งแต่ละตัวมี ::view-transition-old (ภาพสแนปช็อต เนื้อหาที่ถูกแทนที่ซึ่งเทียบได้กับรูปภาพ) และ ::view-transition-new (ตัวแทนที่มีชีวิตอยู่) ค่าเริ่มต้นคือ: การเฟดแบบไขว้ (cross-fade) หน้าเว็บจะ ไม่สามารถโต้ตอบได้ ระหว่างการเปลี่ยนผ่าน

JAVASCRIPT
function applyUpdate(updateDom) {
  if (!document.startViewTransition) {
    updateDom();
    return;
  }
  document.startViewTransition(() => updateDom());
}
การปรับปรุงแบบค่อยเป็นค่อยไป: หากไม่รองรับ การอัปเดตจะถูกนำมาใช้โดยไม่มีแอนิเมชัน

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

CSS
.hero-thumb { view-transition-name: hero; }

::view-transition-old(hero),
::view-transition-new(hero) {
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;
}
กลุ่ม hero ที่ถูกตั้งชื่อจะถูกดักจับ และปรับแต่งระยะเวลาแอนิเมชันของมัน

สิ่งที่เหลืออยู่คือข้อผูกพันด้านการเข้าถึง: prefers-reduced-motion: reduce เป็นสัญญาณบ่งบอกถึงผู้ใช้ที่ไวต่อการเคลื่อนไหว (ความผิดปกติของระบบทรงตัว หรืออาการไมเกรน) การลดไม่ได้หมายถึงการเอาออก: การเฟดด้วย opacity ยังคงยอมรับได้ สิ่งที่ต้องทำให้เป็นกลางคือการเคลื่อนไหวขนาดใหญ่ การซูม และเอฟเฟกต์พารัลแลกซ์ต่างหาก คุณรู้จักการเขียนทับแบบรวมเชิงป้องกันใน CSS อยู่แล้ว (ระยะเวลาเกือบเป็นศูนย์ ครอบคลุมในโมดูลก่อนหน้า) view transitions เพิ่มแนวรบที่สอง: ในฝั่ง JavaScript ให้สอบถาม window.matchMedia("(prefers-reduced-motion: reduce)") ก่อนเริ่มการเปลี่ยนผ่านหรือการเลื่อนแบบมีแอนิเมชัน

JAVASCRIPT
const reduceMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (reduceMotion || !document.startViewTransition) {
  updateDom();
} else {
  document.startViewTransition(() => updateDom());
}
มาตรการป้องกันฝั่ง JavaScript: การอัปเดตจะถูกนำมาใช้โดยไม่มีการมอร์ฟเมื่อผู้ใช้ขอให้ลดการเคลื่อนไหว

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. scroll-snap-stop: always ทำอะไรบนไอเทมของคารูเซล?
    • มันปิดใช้งานความเฉื่อยของการเลื่อนแบบสัมผัส
    • มันบังคับให้หยุดที่ทุกจุดสแนป แม้แต่ระหว่างการปัดเร็ว ๆ
    • มันบล็อกการเลื่อนตามแกนที่ตั้งฉาก
  2. document.startViewTransition() จับสิ่งใดก่อนที่จะรัน callback ของคุณ?
    • การโคลน DOM ปัจจุบันแบบลึก
    • ภาพสแนปช็อตเชิงภาพของสถานะเก่า ซึ่งเล่นซ้ำใน ::view-transition-old
    • HTML ที่ทำเป็นซีเรียลไลซ์ของทั้งหน้าเว็บ
  3. คุณจะเคารพ prefers-reduced-motion ก่อนเริ่ม view transition จาก JavaScript ได้อย่างไร?
    • ทำไม่ได้: ค่ากำหนดนี้อ่านได้ใน CSS เท่านั้น
    • ทดสอบ window.matchMedia('(prefers-reduced-motion: reduce)').matches และนำการอัปเดตมาใช้โดยไม่มี startViewTransition เมื่อมันตรงกัน
    • ส่งออปชัน { motion: 'reduce' } ไปยัง startViewTransition