ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์แท็บที่คล้ายกับคอมโพเนนต์ที่พบในแอป iOS และ Android
ในโพสต์นี้ ฉันต้องการแชร์ความคิดเห็นเกี่ยวกับการสร้างคอมโพเนนต์แท็บสำหรับเว็บ ตอบสนองตามอุปกรณ์ รองรับอินพุตของอุปกรณ์หลายเครื่อง และทำงานในเบราว์เซอร์ได้ ทดลองใช้การสาธิต
หากต้องการดูวิดีโอ โปรดใช้โพสต์นี้ในเวอร์ชัน YouTube
ภาพรวม
แท็บเป็นองค์ประกอบที่พบได้ทั่วไปของระบบการออกแบบ แต่อาจมีได้หลายรูปร่างและ
ตอนแรกมีแท็บบนเดสก์ท็อปที่สร้างขึ้นจากเอลิเมนต์ <frame>
และตอนนี้เรามี
ส่วนประกอบของอุปกรณ์เคลื่อนที่ที่สร้างภาพเคลื่อนไหวตามคุณสมบัติทางฟิสิกส์
ทุกคนพยายามทำแบบเดียวกัน คือการประหยัดพื้นที่
ปัจจุบัน สิ่งสำคัญที่ต้องมีสำหรับประสบการณ์ของผู้ใช้แท็บคือพื้นที่สำหรับการนำทางด้วยปุ่ม ซึ่งจะเปิด/ปิดการมองเห็นเนื้อหาในเฟรมแสดงผล แตกต่างมากมาย พื้นที่เนื้อหาใช้พื้นที่เดียวกัน แต่นำเสนออย่างมีเงื่อนไขตาม ปุ่มที่เลือกไว้ในการนำทาง
![ภาพตัดปะนั้นค่อนข้างยุ่งเหยิงเนื่องจากความหลากหลายของรูปแบบ ที่เว็บได้นำมาใช้กับแนวคิดของส่วนประกอบ](https://web.dev/static/articles/building/a-tabs-component/image/the-collage-is-quite-chao-b6f8cd38f8ddd.png?authuser=1&hl=th)
กลยุทธ์สำหรับเว็บ
สรุปทั้งหมด ผมพบว่าคอมโพเนนต์นี้สามารถสร้างได้ง่ายมาก ฟีเจอร์แพลตฟอร์มเว็บที่สำคัญ 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 กล่องที่มีลูกศรบังคับทิศทางการจับคู่สีซึ่งแสดงพื้นที่ที่เลื่อนและแสดงทิศทางที่พวกเขาจะเลื่อน](https://web.dev/static/articles/building/a-tabs-component/image/3-colorful-boxes-color-m-da0de23b13551.png?authuser=1&hl=th)
มีองค์ประกอบ 2 ประเภทที่เกี่ยวข้องกับการเลื่อน
- หน้าต่าง
กล่องที่มีมิติข้อมูลที่กำหนดไว้ซึ่งมีoverflow
ของพร็อพเพอร์ตี้ - พื้นผิวขนาดใหญ่
ในเลย์เอาต์นี้จะเป็นคอนเทนเนอร์รายการ: Nav ลิงก์ บทความในส่วน และเนื้อหาบทความ
เลย์เอาต์ <snap-tabs>
เลย์เอาต์ระดับบนสุดที่ฉันเลือกคือ Flex (Flexbox) ฉันตั้งทิศทางเป็น
column
ดังนั้นส่วนหัวและส่วนจะเรียงลำดับตามแนวตั้ง นี่คือสิ่งแรก
ซึ่งจะซ่อนทุกอย่างที่มีการซ่อนรายการเพิ่มเติมไว้ ส่วนหัวและ
จะมีการใช้การเลื่อนมากเกินไปในเร็วๆ นี้สำหรับแต่ละโซน
<snap-tabs> <header></header> <section></section> </snap-tabs>
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 againstneeding 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }
ชี้กลับไปที่แผนภาพการเลื่อน 3 แบบสีสันสดใส
<header>
เตรียมพร้อมเป็น (สีชมพู) แล้ว คอนเทนเนอร์แบบเลื่อน<section>
เตรียมพร้อมเป็นแถบเลื่อน (สีน้ำเงิน) คอนเทนเนอร์
เฟรมที่ฉันไฮไลต์ไว้ด้านล่างนี้ VisBug ช่วยให้เรามองเห็นหน้าต่าง สร้างคอนเทนเนอร์แบบเลื่อนได้
![องค์ประกอบส่วนหัวและส่วนมีการวางซ้อน Hotpink ซึ่งระบุพื้นที่ที่องค์ประกอบใช้ไปในคอมโพเนนต์](https://web.dev/static/articles/building/a-tabs-component/image/the-header-section-eleme-dd313d7eb1453.png?authuser=1&hl=th)
เลย์เอาต์ <header>
ของแท็บ
เลย์เอาต์ถัดไปแทบจะเหมือนกัน โดยใช้ Flex เพื่อสร้างเป็นแนวตั้ง
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
.snap-indicator
ควรเดินทางในแนวนอนกับกลุ่มลิงก์ และ
เลย์เอาต์ส่วนหัวนี้จะช่วยกำหนดขั้นตอนดังกล่าว ไม่มีองค์ประกอบที่มีตำแหน่งสัมบูรณ์ที่นี่
![องค์ประกอบ nav และ span.indicator มีการวางซ้อน Hotpink อยู่เพื่ออธิบายพื้นที่ว่างที่องค์ประกอบใช้ไปในคอมโพเนนต์](https://web.dev/static/articles/building/a-tabs-component/image/the-nav-spanindicator-e-eddaf5b04df39.png?authuser=1&hl=th)
ถัดไปคือรูปแบบการเลื่อน เราสามารถแชร์รูปแบบการเลื่อน
ระหว่างพื้นที่เลื่อนแนวนอน 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
<nav> <a></a> <a></a> <a></a> <a></a> </nav>
nav { display: flex; & a { scroll-snap-align: start; display: inline-flex; align-items: center; white-space: nowrap; } }
รูปแบบและขนาดของลิงก์แต่ละรายการเอง ดังนั้นเลย์เอาต์การนำทางจึงเพียงต้องระบุ ทิศทางและทิศทาง ความกว้างที่ไม่ซ้ำกันของรายการนำทางจะทำให้มีการเปลี่ยนแท็บ สนุกเพราะสัญญาณบอกสถานะปรับความกว้างของเป้าหมายใหม่ ขึ้นอยู่กับจำนวน องค์ประกอบอยู่ที่นี่ เบราว์เซอร์จะแสดงแถบเลื่อนหรือไม่
![องค์ประกอบของ Nav มีการวางซ้อน Hotpink อยู่ ซึ่งระบุพื้นที่ว่างที่ใช้ไปในคอมโพเนนต์รวมถึงส่วนที่ล้น](https://web.dev/static/articles/building/a-tabs-component/image/the-elements-the-nav-h-c8efe2b10b412.png?authuser=1&hl=th)
เลย์เอาต์ <section>
ของแท็บ
ส่วนนี้เป็นรายการแบบยืดหยุ่นและต้องเป็นผู้บริโภคหลักของพื้นที่ ทั้งนี้
ต้องสร้างคอลัมน์สำหรับวางบทความด้วย อีกครั้ง รวดเร็ว
สำหรับ CSS 2021 block-size: 100%
จะขยายองค์ประกอบนี้เพื่อให้เป็นไปตาม
ให้มากที่สุดเท่าที่จะเป็นไปได้ จากนั้น สำหรับเลย์เอาต์ จะมีการสร้างชุดของ
คอลัมน์ที่มีความกว้างของ 100%
คอลัมน์หลัก คุณใส่เปอร์เซ็นต์ที่นี่ได้ดีมาก
เพราะเราเขียนข้อจำกัดที่เคร่งครัดกับระดับบนสุด
<section> <article></article> <article></article> <article></article> <article></article> </section>
section { block-size: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 100%; }
เหมือนกับเราพูดว่า "ขยายในแนวตั้งให้มากที่สุด ในแบบที่ไม่สม่ำเสมอ"
(อย่าลืมว่าส่วนหัวที่เราตั้งค่าเป็น flex-shrink: 0
: มีไว้เพื่อป้องกัน
การพุชการขยาย) ซึ่งกำหนดความสูงของแถวสำหรับชุดคอลัมน์ความสูงแบบเต็ม
รูปแบบ auto-flow
บอกตารางกริดให้จัดแบ่งเด็กเป็นแนวนอนเสมอ
ไม่ตัดบรรทัด ไม่ตัดข้อความ หรืออะไรก็ได้ที่เราต้องการ เพื่อแสดงเกินหน้าต่างระดับบนสุด
![องค์ประกอบของบทความมีการวางซ้อน Hotpink ซึ่งระบุพื้นที่ที่ใช้ไปในคอมโพเนนต์และส่วนที่ล้น](https://web.dev/static/articles/building/a-tabs-component/image/the-article-elements-hot-924ae524ee8fb.png?authuser=1&hl=th)
ฉันว่าบางครั้งก็ฟังอะไรไม่ชัดขนาดนี้นะ องค์ประกอบของส่วนนี้คือ ใส่ลงในกล่อง แต่ก็สร้างกล่องขึ้นมาอีกชุดด้วย ผมหวังว่าภาพและ คำอธิบายต่างๆ ช่วยเราได้
เลย์เอาต์ <article>
ของแท็บ
ผู้ใช้ควรเลื่อนเนื้อหาบทความได้และแถบเลื่อนควร แสดงในกรณีที่มีรายการเพิ่มเติมเท่านั้น องค์ประกอบบทความเหล่านี้จัดเรียงเรียบร้อยดี ตำแหน่ง โดยเป็นทั้งลูกเลื่อนและตัวเลื่อน เบราว์เซอร์จะจัดการกับการโต้ตอบกับการสัมผัส เมาส์ และแป้นพิมพ์ที่ยุ่งยาก ให้กับเราที่นี่
<article> <h2></h2> <p></p> <p></p> <h2></h2> <p></p> <p></p> ... </article>
article { scroll-snap-align: start; overflow-y: auto; overscroll-behavior-y: contain; }
ฉันเลือกให้บทความอยู่ในแถบเลื่อนระดับบนสุด ฉันชอบมาก วิธีที่รายการลิงก์การนำทางและองค์ประกอบของบทความสแนปไปยังจุดเริ่มต้นแบบอินไลน์ ของคอนเทนเนอร์แบบเลื่อนได้ตามลำดับ ซึ่งให้ความรู้สึกที่กลมกลืนกัน ความสัมพันธ์
![องค์ประกอบบทความและองค์ประกอบย่อยมีการวางซ้อน Hotpink ซึ่งระบุพื้นที่ว่างที่องค์ประกอบใช้ไปในคอมโพเนนต์และทิศทางที่ซ้อนทับ](https://web.dev/static/articles/building/a-tabs-component/image/the-article-element-its-8afcb8a6817ff.png?authuser=1&hl=th)
บทความเป็นตารางกริดย่อยของตารางกริด และมีการกำหนดขนาดเป็นวิวพอร์ตไว้ล่วงหน้า ที่เราต้องการสำหรับการเลื่อน UX ซึ่งหมายความว่าผมไม่ต้องการความสูงหรือความกว้าง ตรงนี้ ผมเพียงแค่ต้องกำหนดว่า จะล้นมืออย่างไร ฉันตั้งค่า overflow-y เป็นอัตโนมัติ และตรวจจับการโต้ตอบกับการเลื่อนด้วยลักษณะการทำงานของการเลื่อนมากเกินไป
สรุปพื้นที่การเลื่อน 3 ครั้ง
ด้านล่างที่ฉันเลือกในการตั้งค่าระบบเป็น "แสดงแถบเลื่อนเสมอ" ฉันคิดว่า สิ่งที่สำคัญเป็น 2 อย่างก็คือ เลย์เอาต์จะต้องทำงานเมื่อเปิดการตั้งค่านี้ไว้ เนื่องจาก คือช่วยให้ผมตรวจสอบเลย์เอาต์ และการจัดเรียงม้วนกระดาษ
![แถบเลื่อน 3 อันได้รับการตั้งค่าให้แสดง ตอนนี้ใช้พื้นที่ในเลย์เอาต์มากแล้ว และคอมโพเนนต์ของเราก็ยังคงดูดีอยู่](https://web.dev/static/articles/building/a-tabs-component/image/the-3-scrollbars-are-set-ca02ac51f046d.png?authuser=1&hl=th)
ฉันคิดว่าการเห็นรางเลื่อนของแถบเลื่อนในคอมโพเนนต์นี้ จะช่วยให้แสดงได้อย่างชัดเจนถึงตำแหน่ง พื้นที่การเลื่อน ทิศทางที่รองรับ และวิธีโต้ตอบ ระหว่างกัน พิจารณาว่าเฟรมหน้าต่างแบบเลื่อนแต่ละเฟรมนี้มีความยืดหยุ่นหรือ ตารางกริดหลักไปยังเลย์เอาต์
เครื่องมือสำหรับนักพัฒนาเว็บช่วยให้เราแสดงข้อมูลต่อไปนี้
![พื้นที่การเลื่อนจะมีการวางซ้อนเครื่องมือ Flexbox และตารางกริด โดยแบ่งพื้นที่ซึ่งใช้ในคอมโพเนนต์และทิศทางที่วางทับ](https://web.dev/static/articles/building/a-tabs-component/image/the-scroll-areas-grid-f-79f0ae5839bcc.png?authuser=1&hl=th)
เลย์เอาต์ในการเลื่อนเสร็จสมบูรณ์: การสแนป การทำ 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 แท็บ](https://web.dev/static/articles/building/a-tabs-component/image/active-tab-inactive-tab-9604191ea7007.png?authuser=1&hl=th)
ผู้ใช้ขับเคลื่อนภาพเคลื่อนไหวจากการโต้ตอบของตน เห็นความกว้างและ ของตัวบ่งชี้จะเปลี่ยนจากส่วนหนึ่งไปยังส่วนถัดไปการติดตาม เหมาะที่สุดกับการเลื่อนหน้าจอ
คุณอาจไม่เคยสังเกตมาก่อน แต่ผมภูมิใจมากที่การเปลี่ยนสี ระบบจะเลือกรายการนำทางที่ไฮไลต์ไว้
สีเทาอ่อนที่ไม่ได้เลือกจะแสดงถอยกลับมากขึ้นเมื่อหน้าที่ไฮไลต์ รายการมีความคมชัดมากกว่า การเปลี่ยนสีข้อความถือเป็นเรื่องปกติ เช่น เมื่อวางเมาส์เหนือข้อความ และเมื่อเลือกไว้ แต่นี่เป็นการเปลี่ยนสีเมื่อเลื่อนเป็นระดับถัดไป ทำข้อมูลให้ตรงกับตัวบ่งชี้การขีดเส้นใต้แล้ว
วิธีการมีดังนี้
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 เป็นคำสำหรับอุปกรณ์เคลื่อนที่มากกว่า แต่ฉันคิดว่าจุดประสงค์ของ 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 ได้แบบชัดเจนว่าจะต้องเลื่อนองค์ประกอบเข้ามาในมุมมอง
เป็นการจับคู่เล็กๆ ที่น่าประทับใจในบางครั้ง
บทสรุป
เมื่อรู้แล้วว่าฉันทำแบบนั้นได้อย่างไร คุณจะทำอย่างไร ฟีเจอร์นี้ช่วยให้เล่นสนุกขึ้น สถาปัตยกรรมคอมโพเนนต์ ใครที่จะทำให้เวอร์ชันแรกมีช่องใน กรอบการทำงานที่ชอบใช้คืออะไร 🙂
มาเพิ่มความหลากหลายให้กับแนวทางของเราและเรียนรู้วิธีทั้งหมดในการสร้างเนื้อหาบนเว็บกัน สร้างภาพแตก ทวีตฉัน ในเวอร์ชันของคุณ ฉันจะเพิ่มลงในรีมิกซ์ของชุมชน ด้านล่าง
รีมิกซ์ในชุมชน
- @devnook, @rob_dodson และ @DasSurma พร้อมคอมโพเนนต์ของเว็บ: บทความ
- @jhvanderschee ด้วยปุ่ม Codepen