ไฮไลต์ Snap แบบภาพสไลด์

การค้นหาคอนเทนเนอร์สถานะการเลื่อนช่วยให้คุณจัดสไตล์องค์ประกอบตามสถานะการเลื่อนปัจจุบันได้ เช่น องค์ประกอบ "ติด" (ผ่านการจัดตำแหน่งแบบติดหนึบ) หรือ "สแนป" (ผ่านการสแนปการเลื่อน) หรือไม่ ซึ่งจะช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานภาพสไลด์หรือแกลเลอรีที่สามารถแยกแยะรายการที่ใช้งานอยู่ด้วยภาพได้โดยไม่ต้องอาศัย JavaScript Intersection Observer หรือ Listener เหตุการณ์การเลื่อน

การติดตั้งใช้งานหลัก

หากต้องการไฮไลต์รายการที่สแนป คุณต้องสร้างคอนเทนเนอร์สแนปการเลื่อน กำหนดเป้าหมายสแนปเป็นคอนเทนเนอร์สถานะการเลื่อน แล้วค้นหาสถานะนั้นเพื่อจัดสไตล์องค์ประกอบสืบทอด

1. สร้างคอนเทนเนอร์การสแนปการเลื่อน

คอนเทนเนอร์หลักต้องเปิดใช้ scroll-snap-type

<div class="carousel">
  <div class="carousel-item">
    <div class="card">Product 1 content</div>
  </div>
  <div class="carousel-item">
    <div class="card">Product 2 content</div>
  </div>
</div>
.carousel {
  display: flex;
  overflow-x: auto;
  /* MANDATORY: Enable scroll snapping on the container */
  scroll-snap-type: x mandatory;
}

2. กำหนดเป้าหมายการสแนปเป็นคอนเทนเนอร์สถานะการเลื่อน

แต่ละรายการในภาพสไลด์ที่ควรติดตามเพื่อจัดชิดต้องประกาศเป็นคอนเทนเนอร์ scroll-state

.carousel-item {
  /* Define where the item snaps within the container */
  scroll-snap-align: center;
  
  /* MANDATORY: Establish this element as a scroll-state query container */
  container-type: scroll-state;
}

3. ค้นหาสถานะ snapped

เนื่องจากการค้นหาคอนเทนเนอร์จะจัดรูปแบบองค์ประกอบสืบทอด คุณจึงต้องใช้รูปแบบไฮไลต์กับองค์ประกอบภายในเป้าหมายสแนป เนื่องจากคอนเทนเนอร์การเลื่อนตั้งค่าเป็นโอเวอร์โฟลว์ในแกน x ให้ใช้การค้นหา scroll-state(snapped: x)

บังคับ: รวมสไตล์ไว้ใน @media (prefers-reduced-motion: no-preference) เพื่อแสดงเอฟเฟกต์ต่อผู้ใช้ที่ไม่ได้ขอให้ลดการเคลื่อนไหวเท่านั้น คุณอาจคงบางส่วนของเอฟเฟกต์ไว้ได้ ทั้งนี้ขึ้นอยู่กับกรณีการใช้งาน แต่ในกรณีนี้ การ์ดจะกะพริบจากสีขาวเป็นสีน้ำเงินในลักษณะที่อาจทำให้ผู้ใช้บางรายเกิดปัญหา เราจึงปิดใช้เอฟเฟกต์นี้โดยสมบูรณ์

/* Specify transition outside of queries so that it is applied regardless of state.  */
.card {
  transition:
    scale 0.4s cubic-bezier(0.25, 0.8, 0.25, 1),
    background-color 0.4s,
    color 0.4s,
    box-shadow 0.4s;
}
/* 
Only show the effect for users not requesting reduced motion. Disable completely, including the color change, as it causes a flash that may be problematic.
*/
@media (prefers-reduced-motion: no-preference) {
  /* Style the content when its parent .carousel-item is snapped on the x axis */
  @container scroll-state(snapped: x) {
    .card {
      background: #007bff;
      color: white;
      scale: 1.15;
      box-shadow: 0 10px 25px rgba(0, 123, 255, 0.3);
    }
  }
}

/* MANDATORY Copy-Paste Safety: Disable highlight scaling/flashing for motion sensitive users */
@media (prefers-reduced-motion: reduce) {
  .card {
    transition: none !important;
    scale: 1 !important;
  }
}

ข้อบ่งชี้ snapped สามารถค้นหาแกนที่เฉพาะเจาะจงได้ ได้แก่ x, y, inline, block หรือ both

การช่วยเหลือพิเศษ

AVOID: การใช้ scroll-state กับองค์ประกอบแบบอินเทอร์แอกทีฟ

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

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

กลยุทธ์สำรอง

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

การตรวจจับฟีเจอร์

คุณสามารถใช้ @supports เพื่อปรับปรุงเฉพาะเบราว์เซอร์ที่รองรับได้ดังนี้

@supports (container-type: scroll-state) {
  /* Enhancement styles here */
}

JavaScript สำรอง

หากไฮไลต์มีความสำคัญต่อประสบการณ์ของผู้ใช้ ให้ใช้ IntersectionObserver เพื่อระบุรายการที่สแนป ปรับพื้นที่ที่สังเกตได้ให้เป็นส่วนบางๆ ตรงกลางภาพสไลด์โดยระบุ rootMargin ที่มีค่าอินไลน์เป็นลบ เช่น หากต้องการพิจารณาว่าองค์ประกอบหนึ่งๆ มีการตัดกันหากอยู่ในส่วนกลาง 2% ของภาพสไลด์ ให้ตั้งค่า rootMargin เป็น "0px -49%"

// Optional: detect support and apply a JS-based fallback
if (!CSS.supports('container-type', 'scroll-state')) {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      // Toggle a class based on intersection
      entry.target.classList.toggle('is-snapped', entry.isIntersecting);
    });
  }, {
    root: document.querySelector(".carousel"),
    // Carousel item intersects if any part of the carousel item is in the middle 2% of the carousel.
    rootMargin: "0px -49%"
  });

  document.querySelectorAll('.carousel-item').forEach(item => {
    observer.observe(item);
  });
}