การสร้างคอมโพเนนต์แท็บ

ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์แท็บที่คล้ายกับคอมโพเนนต์ที่พบในแอป iOS และ Android

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

สาธิต

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

ภาพรวม

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

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

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

กลยุทธ์สำหรับเว็บ

สรุปทั้งหมด ผมพบว่าคอมโพเนนต์นี้สามารถสร้างได้ง่ายมาก ฟีเจอร์แพลตฟอร์มเว็บที่สำคัญ 2-3 ประการ ได้แก่

  • scroll-snap-pointsเพื่อการปัดอย่างสวยงามและการโต้ตอบกับแป้นพิมพ์ด้วย ตำแหน่งหยุดการเลื่อนที่เหมาะสม
  • Deep Link ผ่านแฮช URL สำหรับ เบราว์เซอร์ที่มีการแฮนเดิลการเลื่อนในหน้าเว็บ รวมถึงการรองรับการแชร์
  • รองรับโปรแกรมอ่านหน้าจอพร้อมมาร์กอัปองค์ประกอบ <a> และ id="#hash"
  • prefers-reduced-motion สำหรับการเปิดใช้การเปลี่ยนแบบครอสเฟดและการทันใจ การเลื่อนในหน้าเว็บ
  • ฟีเจอร์เว็บ @scroll-timeline ฉบับร่างสำหรับการขีดเส้นใต้แบบไดนามิกและ เปลี่ยนสีแท็บที่เลือก

HTML

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

มีสมาชิกด้านเนื้อหาเชิงโครงสร้างบางส่วน เช่น ลิงก์และ :target พ ต้องมีรายการลิงก์ ซึ่ง <nav> เหมาะอย่างยิ่ง และลิสต์ <article> ซึ่ง <section> เหมาะอย่างยิ่ง แฮชลิงก์แต่ละรายการ จะตรงกับส่วน ให้เบราว์เซอร์เลื่อนสิ่งต่างๆ ผ่านแท็ก Anchor

การคลิกปุ่มลิงก์ เพื่อเลื่อนไปยังเนื้อหาที่โฟกัส

ตัวอย่างเช่น การคลิกลิงก์จะโฟกัสที่บทความ :target โดยอัตโนมัติใน Chrome 89, ไม่ต้องมี JS จากนั้นผู้ใช้สามารถเลื่อนเนื้อหาของบทความ อุปกรณ์อินพุตของตนเช่นเคย เป็นเนื้อหาโดยไม่เสียค่าใช้จ่ายตามที่ระบุไว้ใน มาร์กอัป

ฉันใช้มาร์กอัปต่อไปนี้เพื่อจัดระเบียบแท็บ

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

ฉันสามารถสร้างการเชื่อมต่อระหว่างองค์ประกอบ <a> และ <article> ด้วย href และ id พร็อพเพอร์ตี้แบบนี้:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

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

เลย์เอาต์แบบเลื่อน

พื้นที่การเลื่อนในคอมโพเนนต์นี้มี 3 ประเภทที่แตกต่างกัน

  • การนำทาง (สีชมพู) อยู่ในแนวนอน เลื่อนได้
  • พื้นที่เนื้อหา (สีน้ำเงิน) อยู่ในแนวนอน เลื่อนได้
  • แต่ละรายการในบทความ (สีเขียว) เป็นแนวตั้ง เลื่อนได้
กล่องหลากสี 3 กล่องที่มีลูกศรบังคับทิศทางการจับคู่สีซึ่งแสดงพื้นที่ที่เลื่อนและแสดงทิศทางที่พวกเขาจะเลื่อน

มีองค์ประกอบ 2 ประเภทที่เกี่ยวข้องกับการเลื่อน

  1. หน้าต่าง
    กล่องที่มีมิติข้อมูลที่กำหนดไว้ซึ่งมีoverflow ของพร็อพเพอร์ตี้
  2. พื้นผิวขนาดใหญ่
    ในเลย์เอาต์นี้จะเป็นคอนเทนเนอร์รายการ: Nav ลิงก์ บทความในส่วน และเนื้อหาบทความ

เลย์เอาต์ <snap-tabs>

เลย์เอาต์ระดับบนสุดที่ฉันเลือกคือ Flex (Flexbox) ฉันตั้งทิศทางเป็น column ดังนั้นส่วนหัวและส่วนจะเรียงลำดับตามแนวตั้ง นี่คือสิ่งแรก ซึ่งจะซ่อนทุกอย่างที่มีการซ่อนรายการเพิ่มเติมไว้ ส่วนหัวและ จะมีการใช้การเลื่อนมากเกินไปในเร็วๆ นี้สำหรับแต่ละโซน

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

ชี้กลับไปที่แผนภาพการเลื่อน 3 แบบสีสันสดใส

  • <header> เตรียมพร้อมเป็น (สีชมพู) แล้ว คอนเทนเนอร์แบบเลื่อน
  • <section> เตรียมพร้อมเป็นแถบเลื่อน (สีน้ำเงิน) คอนเทนเนอร์

เฟรมที่ฉันไฮไลต์ไว้ด้านล่างนี้ VisBug ช่วยให้เรามองเห็นหน้าต่าง สร้างคอนเทนเนอร์แบบเลื่อนได้

องค์ประกอบส่วนหัวและส่วนมีการวางซ้อน Hotpink ซึ่งระบุพื้นที่ที่องค์ประกอบใช้ไปในคอมโพเนนต์

เลย์เอาต์ <header> ของแท็บ

เลย์เอาต์ถัดไปแทบจะเหมือนกัน โดยใช้ Flex เพื่อสร้างเป็นแนวตั้ง

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator ควรเดินทางในแนวนอนกับกลุ่มลิงก์ และ เลย์เอาต์ส่วนหัวนี้จะช่วยกำหนดขั้นตอนดังกล่าว ไม่มีองค์ประกอบที่มีตำแหน่งสัมบูรณ์ที่นี่

องค์ประกอบ nav และ span.indicator มีการวางซ้อน Hotpink อยู่เพื่ออธิบายพื้นที่ว่างที่องค์ประกอบใช้ไปในคอมโพเนนต์

ถัดไปคือรูปแบบการเลื่อน เราสามารถแชร์รูปแบบการเลื่อน ระหว่างพื้นที่เลื่อนแนวนอน 2 ส่วน (ส่วนหัวและส่วน) ผมจึงสร้างยูทิลิตี ชั้นเรียน .scroll-snap-x

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

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

เลย์เอาต์ <nav> ในส่วนหัวของแท็บ

ลิงก์การนำทางจะต้องวางเรียงกันในแนวตั้ง โดยไม่ต้องขึ้นบรรทัดใหม่ ตรงกลาง และรายการลิงก์แต่ละรายการควรสแนปไปยังคอนเทนเนอร์ Scroll-Snap Swift ใช้ได้กับ CSS ปี 2021

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

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

องค์ประกอบของ Nav มีการวางซ้อน Hotpink อยู่ ซึ่งระบุพื้นที่ว่างที่ใช้ไปในคอมโพเนนต์รวมถึงส่วนที่ล้น

เลย์เอาต์ <section> ของแท็บ

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

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

เหมือนกับเราพูดว่า "ขยายในแนวตั้งให้มากที่สุด ในแบบที่ไม่สม่ำเสมอ" (อย่าลืมว่าส่วนหัวที่เราตั้งค่าเป็น flex-shrink: 0: มีไว้เพื่อป้องกัน การพุชการขยาย) ซึ่งกำหนดความสูงของแถวสำหรับชุดคอลัมน์ความสูงแบบเต็ม รูปแบบ auto-flow บอกตารางกริดให้จัดแบ่งเด็กเป็นแนวนอนเสมอ ไม่ตัดบรรทัด ไม่ตัดข้อความ หรืออะไรก็ได้ที่เราต้องการ เพื่อแสดงเกินหน้าต่างระดับบนสุด

องค์ประกอบของบทความมีการวางซ้อน Hotpink ซึ่งระบุพื้นที่ที่ใช้ไปในคอมโพเนนต์และส่วนที่ล้น

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

เลย์เอาต์ <article> ของแท็บ

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

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

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

องค์ประกอบบทความและองค์ประกอบย่อยมีการวางซ้อน Hotpink ซึ่งระบุพื้นที่ว่างที่องค์ประกอบใช้ไปในคอมโพเนนต์และทิศทางที่ซ้อนทับ

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

สรุปพื้นที่การเลื่อน 3 ครั้ง

ด้านล่างที่ฉันเลือกในการตั้งค่าระบบเป็น "แสดงแถบเลื่อนเสมอ" ฉันคิดว่า สิ่งที่สำคัญเป็น 2 อย่างก็คือ เลย์เอาต์จะต้องทำงานเมื่อเปิดการตั้งค่านี้ไว้ เนื่องจาก คือช่วยให้ผมตรวจสอบเลย์เอาต์ และการจัดเรียงม้วนกระดาษ

แถบเลื่อน 3 อันได้รับการตั้งค่าให้แสดง ตอนนี้ใช้พื้นที่ในเลย์เอาต์มากแล้ว และคอมโพเนนต์ของเราก็ยังคงดูดีอยู่

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

เครื่องมือสำหรับนักพัฒนาเว็บช่วยให้เราแสดงข้อมูลต่อไปนี้

วันที่ พื้นที่การเลื่อนจะมีการวางซ้อนเครื่องมือ Flexbox และตารางกริด โดยแบ่งพื้นที่ซึ่งใช้ในคอมโพเนนต์และทิศทางที่วางทับ
Chromium Devtools แสดงเลย์เอาต์องค์ประกอบ Flexbox Nav ที่เต็มไปด้วยองค์ประกอบ Anchor เลย์เอาต์ส่วนตารางกริดที่เต็มไปด้วยองค์ประกอบบทความและบทความ องค์ประกอบที่เต็มไปด้วยย่อหน้าและองค์ประกอบส่วนหัว

เลย์เอาต์ในการเลื่อนเสร็จสมบูรณ์: การสแนป การทำ Deep Link และแป้นพิมพ์ สามารถเข้าถึงได้ รากฐานที่มั่นคงสำหรับการเพิ่มประสิทธิภาพ UX รูปแบบ และความน่าสนใจ

ฟีเจอร์เด่น

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

แอนิเมชัน

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

ฉันจะลิงก์แท็บที่ขีดเส้นใต้กับตำแหน่งการเลื่อนบทความ การสแนปไม่เป็นเช่นนั้น การจัดแนวที่ดีเท่านั้น ยังเป็นการตรึงจุดเริ่มต้นและจุดสิ้นสุดของภาพเคลื่อนไหวด้วย การทำเช่นนี้จะเก็บ <nav> ซึ่งทำหน้าที่เหมือน แผนที่ขนาดเล็ก ซึ่งเชื่อมต่อกับเนื้อหา เราจะตรวจสอบค่ากำหนดการเคลื่อนไหวของผู้ใช้จากทั้ง CSS และ JS มี จะมีสถานที่ดีๆ 2-3 แห่งที่ควรพิจารณา

ลักษณะการทำงานของการเลื่อน

คุณมีโอกาสปรับปรุงลักษณะการเคลื่อนไหวของทั้ง :target และ element.scrollIntoView() ซึ่งจะแสดงทันทีโดยค่าเริ่มต้น เบราว์เซอร์เพียงแค่ตั้งค่า ตำแหน่งการเลื่อน ถ้าเราจะเปลี่ยนไปใช้ตำแหน่งการเลื่อนนั้น แทนที่จะกะพริบตา

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

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

สัญญาณบอกสถานะแท็บ

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

ใน Chromium Devtools ฉันสามารถสลับค่ากำหนดและแสดง สไตล์การเปลี่ยนที่หลากหลาย ฉันสนุกไปกับการสร้างคอนเทนต์นี้มาก

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

ฉันซ่อน .snap-indicator เมื่อผู้ใช้ต้องการลดการเคลื่อนไหวเนื่องจากฉันไม่ต้องการ ต้องการอีก จากนั้นแทนที่ด้วยสไตล์ border-block-end และ transition ในการโต้ตอบแท็บด้วย ให้สังเกตที่รายการนำทางที่ใช้งานอยู่ มีเพียงไฮไลต์ที่ขีดเส้นใต้แบรนด์ แต่สีข้อความก็เข้มกว่าด้วย องค์ประกอบที่ใช้งานอยู่มีคอนทราสต์ของสีข้อความสูงกว่าและเน้นแสงใต้ที่สว่าง

การใช้ CSS เพิ่มมาอีกไม่กี่บรรทัดก็จะทำให้ผู้ชมรู้สึกว่าคุณมองเห็นแล้ว (ในลักษณะที่ เราคำนึงถึงค่ากำหนดการเคลื่อนไหวของพวกเขาอย่างรอบคอบ) ฉันชอบมาก

@scroll-timeline

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

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
);

ก่อนอื่นฉันจะตรวจสอบค่ากำหนดการเคลื่อนไหวของผู้ใช้จาก JavaScript หากผลลัพธ์ของ คือ false ซึ่งหมายความว่าผู้ใช้ต้องการลดการเคลื่อนไหว เราจะไม่เรียกใช้ ของการเลื่อนลิงก์เอฟเฟกต์การเคลื่อนไหว

if (motionOK) {
  // motion based animation code
}

ในขณะที่เขียนบทความนี้ เบราว์เซอร์สนับสนุนสำหรับ @scroll-timeline ไม่ใช่รายการใดเลย เป็น ข้อกำหนดร่าง ที่มีเพียง การใช้งานทดสอบ แต่มี Polyfill ที่ผมใช้ใน การสาธิต

ScrollTimeline

แม้ว่าทั้ง CSS และ JavaScript จะสามารถสร้างไทม์ไลน์แบบเลื่อนได้ แต่ฉันเลือกใช้ JavaScript เพื่อให้ฉันสามารถใช้การวัดองค์ประกอบแบบเรียลไทม์ในภาพเคลื่อนไหวได้

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

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

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

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

คีย์เฟรมแบบไดนามิก

มีวิธีการ CSS แบบประกาศที่แท้จริงที่ได้ผลจริงๆ ในการสร้างภาพเคลื่อนไหวด้วย @scroll-timeline แต่ภาพเคลื่อนไหวที่ฉันเลือกทำมีลักษณะไดนามิกมากเกินไป ไม่มี วิธีเปลี่ยนความกว้างระหว่าง auto และไม่มีทางจะสร้างแบบไดนามิกได้ จำนวนของคีย์เฟรมตามความยาวของเด็ก

JavaScript รู้วิธีดึงข้อมูลนั้น ดังนั้นเราจะปรับปรุง และเก็บค่าที่คำนวณไว้ขณะรันไทม์

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

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

ต่อไปนี้คือตัวอย่างเอาต์พุตตามแบบอักษรและค่ากำหนดเบราว์เซอร์ของฉัน:

คีย์เฟรมแปลภาษา:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

คีย์เฟรมความกว้าง:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

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

แท็บที่ใช้งานอยู่และแท็บที่ไม่ได้ใช้งานจะแสดงพร้อมการวางซ้อน VisBug ที่แสดงคะแนนคอนทราสต์ที่ผ่านแล้วสำหรับทั้ง 2 แท็บ

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

คุณอาจไม่เคยสังเกตมาก่อน แต่ผมภูมิใจมากที่การเปลี่ยนสี ระบบจะเลือกรายการนำทางที่ไฮไลต์ไว้

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

วิธีการมีดังนี้

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

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

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

คีย์เฟรมที่มีสี var(--text-active-color) จะไฮไลต์ลิงก์ดังกล่าว และ หรือไม่เช่นนั้น จะเป็นสีข้อความมาตรฐาน ลูปแบบซ้อนกันในนี้ ตรงไปตรงมา เนื่องจากลูปด้านนอกคือรายการการนำทางแต่ละรายการ และลูปภายในแต่ละชิ้น คีย์เฟรมส่วนตัวของ navitem ฉันตรวจสอบว่าองค์ประกอบลูปด้านนอกเหมือนกับ ลูปด้านใน 1 เส้น และใช้เพื่อให้ทราบเมื่อมีการเลือก

ฉันสนุกกับการเขียนเรื่องนี้มากเลย ขอบคุณมาก

การเพิ่มประสิทธิภาพ JavaScript มากยิ่งขึ้น

โปรดทราบว่าหลักๆ ที่ผมแสดงที่นี่ทำงานโดย JavaScript คราวนี้มาดูกันว่าเราจะปรับปรุงให้ดีขึ้นได้อย่างไรเมื่อ JS พร้อมใช้งาน

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

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

สิ้นสุดการซิงค์การเลื่อน

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

นี่คือวิธีการรอให้การเลื่อนสิ้นสุด: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

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

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

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

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

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

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

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

บทสรุป

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

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

รีมิกซ์ในชุมชน