การสร้างคอมโพเนนต์ตัวเลื่อนสื่อ

ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้าง ScrollView แนวนอนที่ปรับเปลี่ยนตามอุปกรณ์สำหรับทีวี โทรศัพท์ เดสก์ท็อป ฯลฯ

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

การสาธิต

หากต้องการดูวิดีโอ โปรดดูโพสต์นี้ใน YouTube

ภาพรวม

เราจะสร้างเลย์เอาต์การเลื่อนแนวนอนซึ่งออกแบบมาเพื่อโฮสต์ภาพปกของสื่อหรือผลิตภัณฑ์ คอมโพเนนต์เริ่มต้นเป็นเพียง<ul>รายการธรรมดา แต่ ได้รับการเปลี่ยนโฉมด้วย CSS ให้กลายเป็นประสบการณ์การเลื่อนที่ราบรื่นและน่าพึงพอใจ ซึ่งแสดง รูปภาพและสแนปรูปภาพเหล่านั้นไปยังตารางกริด มีการเพิ่ม JavaScript เพื่ออำนวยความสะดวกในการโต้ตอบกับดัชนีการหมุนเวียน ซึ่งจะช่วยให้ผู้ใช้แป้นพิมพ์ข้ามการไปยังรายการต่างๆ กว่า 100 รายการได้ นอกจากนี้ เรายังใช้ Media Query เวอร์ชันทดลอง prefers-reduced-data เพื่อเปลี่ยน ตัวเลื่อนสื่อให้เป็นประสบการณ์ตัวเลื่อนชื่อที่มีน้ำหนักเบา

เริ่มต้นด้วยมาร์กอัปที่เข้าถึงได้

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

ส่งรายการที่มีองค์ประกอบ <ul> ดังนี้

<ul class="horizontal-media-scroller">
  <li></li>
  <li></li>
  <li></li>
  ...
<ul>

ทำให้รายการโต้ตอบได้ด้วยองค์ประกอบ <a> ดังนี้

<li>
  <a href="#">
    ...
  </a>
</li>

ใช้แท็ก <figure> เพื่อแสดงรูปภาพและคำบรรยายภาพอย่างมีความหมาย

<figure>
  <picture>
    <img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
  </picture>
  <figcaption>Legends</figcaption>
</figure>

โปรดสังเกตแอตทริบิวต์ alt และ loading ใน <img> ข้อความแสดงแทนสำหรับแถบเลื่อนสื่อเป็นโอกาสด้าน UX ที่จะช่วยให้บริบทเพิ่มเติมแก่ภาพขนาดย่อ หรือเป็นข้อความสำรองหากรูปภาพโหลดไม่สำเร็จ หรือเป็น UI ที่อ่านออกเสียงสำหรับผู้ใช้ที่ใช้เทคโนโลยีความช่วยเหลือพิเศษ เช่น โปรแกรมอ่านหน้าจอ ดูข้อมูลเพิ่มเติมได้ที่กฎทอง 5 ข้อสำหรับข้อความแสดงแทนที่ปฏิบัติตามข้อกำหนด

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

รองรับค่ากำหนดรูปแบบสีของผู้ใช้

ใช้ color-scheme เป็นแท็ก <meta> เพื่อส่งสัญญาณไปยังเบราว์เซอร์ว่าหน้าเว็บต้องการทั้งรูปแบบ User-Agent ที่มีให้สำหรับธีมสว่างและธีมมืด โดยเป็นโหมดมืดหรือโหมดสว่างฟรี ขึ้นอยู่กับว่าคุณมองในมุมใด

<meta name="color-scheme" content="dark light">

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

ดูข้อมูลเพิ่มเติมได้จาก Thomas Steiner ที่ https://web.dev/color-scheme/

เพิ่มเนื้อหา

เมื่อพิจารณาโครงสร้างเนื้อหาของ ul > li > a > figure > picture > img ข้างต้น งานถัดไปคือการเพิ่มรูปภาพและชื่อเพื่อเลื่อนดู ฉันได้ใส่รูปภาพและข้อความตัวยึดตำแหน่งแบบคงที่ไว้ในเดโมนี้ แต่คุณสามารถใช้แหล่งข้อมูลโปรดของคุณได้

เพิ่มสไตล์ด้วย CSS

ตอนนี้ถึงเวลาที่ CSS จะนำรายการเนื้อหาทั่วไปนี้ไปเปลี่ยนเป็น ประสบการณ์การใช้งานแล้ว Netflix, App Store และเว็บไซต์และแอปอื่นๆ อีกมากมายใช้พื้นที่เลื่อนแนวนอนเพื่อจัดหมวดหมู่และตัวเลือกต่างๆ ไว้ในวิวพอร์ต

การสร้างเลย์เอาต์ของตัวเลื่อน

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

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

คอนเทนเนอร์อนุญาตให้ลบล้างขนาดคอลัมน์โดยระบุขนาดเริ่มต้นเป็น พร็อพเพอร์ตี้ที่กำหนดเอง เลย์เอาต์ตารางกริดนี้มีข้อจำกัดเกี่ยวกับขนาดคอลัมน์ โดยจะ จัดการเฉพาะระยะห่างและทิศทางเท่านั้น

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
  margin: 0;
}

จากนั้นองค์ประกอบ <picture> จะใช้พร็อพเพอร์ตี้ที่กำหนดเองเพื่อสร้างสัดส่วนภาพพื้นฐานของเรา ซึ่งก็คือสี่เหลี่ยม

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

เพิ่มสไตล์อีกเพียงเล็กน้อยเพื่อสร้างโครงร่างของแถบเลื่อนสื่อให้เสร็จสมบูรณ์

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  & > li {
    display: inline-block; /* removes the list-item bullet */
  }

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

การตั้งค่า overflow จะตั้งค่า <ul> เพื่ออนุญาตการเลื่อนและการไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ ผ่านรายการ จากนั้นระบบจะนำ ::marker ขององค์ประกอบย่อยโดยตรงแต่ละรายการ <li> ออก โดยรับประเภทการแสดงผลใหม่เป็น inline-block

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

img {
  /* smash into whatever box it's in */
  inline-size: 100%;
  block-size: 100%;

  /* don't squish but do cover the space */
  object-fit: cover;

  /* soften the edges */
  border-radius: 1ex;
  overflow: hidden;

  /* if empty, show a gradient placeholder */
  background-image:
    linear-gradient(
      to bottom,
      hsl(0 0% 40%),
      hsl(0 0% 20%)
    );
}

ระยะห่างจากขอบของการเลื่อน

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

หากต้องการสร้างเลย์เอาต์การเลื่อนแบบขอบจรดขอบที่สอดคล้องกับตัวอักษร และเส้นเลย์เอาต์ ให้ใช้ padding ที่ตรงกับ scroll-padding ดังนี้

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}

แก้ไขข้อบกพร่องเกี่ยวกับการเว้นวรรคการเลื่อนแนวนอน ด้านบนแสดงให้เห็นว่าการเว้นวรรคคอนเทนเนอร์ที่เลื่อนได้ควรทำได้ง่ายเพียงใด แต่ก็ยังมีปัญหาความเข้ากันได้ที่ยังไม่ได้รับการแก้ไข (ได้รับการแก้ไขแล้วใน Chromium 91 ขึ้นไป) ดูประวัติโดยย่อได้ที่นี่ แต่โดยสรุปคือการเว้นวรรคไม่ได้คำนึงถึงในมุมมองการเลื่อนเสมอไป

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

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

.horizontal-media-scroller > li:last-of-type figure {
  position: relative;

  &::after {
    content: "";
    position: absolute;

    inline-size: var(--gap);
    block-size: 100%;

    inset-block-start: 0;
    inset-inline-end: calc(var(--gap) * -1);
  }
}

การใช้พร็อพเพอร์ตี้เชิงตรรกะช่วยให้เครื่องมือเลื่อนสื่อทำงานได้ในโหมดการเขียน และทิศทางของเอกสาร

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

คอนเทนเนอร์ที่เลื่อนได้ซึ่งมีเนื้อหาล้นสามารถกลายเป็น Viewport ที่สแนปได้ด้วย CSS บรรทัดเดียว จากนั้นบุตรหลานจะระบุวิธีที่ต้องการจัดแนวกับ Viewport นั้น

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block-end: calc(var(--gap) / 2);

  scroll-snap-type: inline mandatory;

  & figure {
    scroll-snap-align: start;
  }
}

โฟกัส

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

.horizontal-media-scroller a {
  outline-offset: 12px;

  &:focus {
    outline-offset: 7px;
  }

  @media (prefers-reduced-motion: no-preference) {
    & {
      transition: outline-offset .25s ease;
    }
  }
}

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

ดัชนีเคลื่อนที่

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

โดยมีไอเทม 300 รายการในแถบเลื่อนแรกของเดโม เราสามารถทำได้ดีกว่าการให้ผู้ใช้ เลื่อนผ่านทุกรายการเพื่อไปยังส่วนถัดไป

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

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

การสาธิตนี้จะค้นหาเอกสารสำหรับองค์ประกอบที่เลื่อนได้ และเรียกใช้ฟังก์ชัน rovingIndex() สำหรับแต่ละองค์ประกอบ ส่ง rovingIndex() องค์ประกอบเพื่อรับประสบการณ์การย้ายโฟกัส เช่น คอนเทนเนอร์รายการ และตัวเลือกการค้นหาเป้าหมาย ในกรณีที่ เป้าหมายโฟกัสไม่ใช่ลูกหลานโดยตรง

document.querySelectorAll('.horizontal-media-scroller')
  .forEach(scroller =>
    rovingIndex({
      element: scroller,
      target: 'a',
}))

ดูข้อมูลเพิ่มเติมเกี่ยวกับเอฟเฟกต์นี้ได้ที่ไลบรารีโอเพนซอร์ส roving-ux

สัดส่วนภาพ

ในขณะที่เขียนโพสต์นี้ การรองรับ aspect-ratio อยู่เบื้องหลัง ฟีเจอร์ใน Firefox แต่พร้อมใช้งานในเบราว์เซอร์ Chromium หรือกล่องรับสัญญาณ เนื่องจากเลย์เอาต์ตารางของตัวเลื่อนสื่อระบุเฉพาะทิศทางและระยะห่างเท่านั้น การปรับขนาดจึงเปลี่ยนแปลงได้ภายใน Media Query ซึ่งฟีเจอร์จะตรวจสอบการรองรับสัดส่วนภาพ การเพิ่มประสิทธิภาพแบบค่อยเป็นค่อยไปในตัวเลื่อนสื่อแบบไดนามิกมากขึ้น

กล่องที่มีสัดส่วนภาพ 4:4 จะแสดงข้างสัดส่วนภาพอื่นๆ ที่ใช้คือ 16:9 และ 4:3

@supports (aspect-ratio: 1) {
  .horizontal-media-scroller figure > picture {
    inline-size: auto; /* for a block-size driven ratio */
    aspect-ratio: 1; /* boxes by default */

    @nest section:nth-child(2) & {
      aspect-ratio: 16/9;
    }

    @nest section:nth-child(3) & {
      /* double the size of the others */
      block-size: calc(var(--size) * 2);
      aspect-ratio: 4/3;

      /* adjust size to fit more items into the viewport */
      @media (width <= 480px) {
        block-size: calc(var(--size) * 1.5);
      }
    }
  }
}

หากเบราว์เซอร์รองรับไวยากรณ์ aspect-ratio ระบบจะอัปเกรดรูปภาพในแถบเลื่อนสื่อเป็นขนาด aspect-ratio การใช้ไวยากรณ์การซ้อนแบบร่าง รูปภาพแต่ละรูปจะเปลี่ยนสัดส่วนภาพตามว่าเป็นแถวที่ 1, 2 หรือ 3 ไวยากรณ์ของ Nest ยังช่วยให้คุณปรับวิวพอร์ตเล็กๆ น้อยๆ ได้ด้วย โดยจะอยู่ร่วมกับตรรกะการกำหนดขนาดอื่นๆ

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

ต้องการลดการใช้อินเทอร์เน็ต

แม้ว่าเทคนิคถัดไปนี้จะใช้ได้หลัง Flag ใน Canary เท่านั้น แต่ฉันก็อยากจะแชร์วิธีที่ฉันใช้เพื่อประหยัดเวลาในการโหลดหน้าเว็บและปริมาณการใช้อินเทอร์เน็ตได้อย่างมากด้วย CSS เพียงไม่กี่บรรทัด prefers-reduced-dataMedia Query จาก ระดับ 5 ช่วยให้คุณถามได้ว่าอุปกรณ์อยู่ใน สถานะลดการใช้ข้อมูลใดๆ หรือไม่ เช่น โหมดประหยัดอินเทอร์เน็ต หากเป็นเช่นนั้น เราจะแก้ไข เอกสาร และในกรณีนี้คือซ่อนรูปภาพ

ALT_TEXT_HERE

figure {
  @media (prefers-reduced-data: reduce) {
    & {
      min-inline-size: var(--size);

      & > picture {
        display: none;
      }
    }
  }
}

เนื้อหายังคงนำทางได้ แต่จะไม่มีค่าใช้จ่ายในการดาวน์โหลดรูปภาพขนาดใหญ่ นี่คือเว็บไซต์ก่อนเพิ่ม prefers-reduced-data CSS

(คำขอ 7 รายการ ทรัพยากร 100 KB ใน 131 มิลลิวินาที)

ALT_TEXT_HERE

ประสิทธิภาพของเว็บไซต์หลังจากเพิ่ม prefers-reduced-data CSS มีดังนี้

ALT_TEXT_HERE

(คำขอ 71 รายการ ทรัพยากร 1.2 MB ใน 1.07 วินาที)

คำขอน้อยลง 64 รายการ ซึ่งก็คือรูปภาพประมาณ 60 รูปภายใน Viewport (การทดสอบบนจอแสดงผลแบบจอกว้าง) ของแท็บเบราว์เซอร์นี้ การเพิ่มประสิทธิภาพการโหลดหน้าเว็บประมาณ 80% และข้อมูล 10% ผ่านสาย CSS ที่มีประสิทธิภาพมาก

บทสรุป

ตอนนี้คุณรู้วิธีที่ฉันทำแล้ว คุณจะทำอย่างไร 🙂

มาลองใช้แนวทางที่หลากหลายและเรียนรู้วิธีต่างๆ ในการสร้างสรรค์บนเว็บกัน สร้าง Codepen หรือโฮสต์เดโมของคุณเอง แล้วทวีตมาหาฉันพร้อมกับเดโมนั้น จากนั้นฉันจะเพิ่มเดโมลงในส่วน รีมิกซ์ของชุมชนด้านล่าง

แหล่งที่มา

รีมิกซ์ของชุมชน

ยังไม่มีอะไรให้ดูที่นี่