iOS और Android ऐप्लिकेशन में मौजूद टैब कॉम्पोनेंट जैसा कॉम्पोनेंट बनाने के तरीके के बारे में बुनियादी जानकारी.
इस पोस्ट में, मैं वेब के लिए टैब कॉम्पोनेंट बनाने के बारे में अपने विचार शेयर करना चाहता हूं. यह कॉम्पोनेंट रिस्पॉन्सिव है, कई डिवाइस इनपुट के साथ काम करता है, और सभी ब्राउज़र पर काम करता है. डेमो आज़माएं.
अगर आपको वीडियो देखना पसंद है, तो इस पोस्ट का YouTube वर्शन यहां देखें:
खास जानकारी
टैब, डिज़ाइन सिस्टम का एक सामान्य कॉम्पोनेंट है. हालांकि, यह कई तरह के आकार और फ़ॉर्मैट में हो सकता है. पहले, <frame> एलिमेंट पर बने डेस्कटॉप टैब थे. अब हमारे पास मोबाइल के लिए ऐसे कॉम्पोनेंट हैं जो फ़िज़िक्स प्रॉपर्टी के आधार पर कॉन्टेंट को ऐनिमेट करते हैं.
इन सभी का मकसद एक ही है: स्टोरेज में जगह बचाना.
आज के समय में, टैब के उपयोगकर्ता अनुभव के लिए ज़रूरी चीज़ों में, बटन नेविगेशन एरिया शामिल है. इससे डिसप्ले फ़्रेम में कॉन्टेंट को टॉगल करके देखा जा सकता है. कॉन्टेंट के कई अलग-अलग सेक्शन एक ही स्पेस शेयर करते हैं. हालांकि, इन्हें नेविगेशन में चुने गए बटन के आधार पर, शर्त के मुताबिक दिखाया जाता है.
वेब टैक्टिक्स
कुल मिलाकर, मुझे इस कॉम्पोनेंट को बनाना काफ़ी आसान लगा. इसकी वजह वेब प्लैटफ़ॉर्म की कुछ अहम सुविधाएं हैं:
scroll-snap-points, ताकि स्वाइप करने और कीबोर्ड से इंटरैक्ट करने की सुविधा को बेहतर बनाया जा सके. साथ ही, स्क्रोल करने पर सही जगह पर रुका जा सके- ब्राउज़र में मैनेज किए जाने वाले इन-पेज स्क्रोल ऐंकरिंग और शेयर करने की सुविधा के लिए, यूआरएल हैश के ज़रिए डीप लिंक
<a>औरid="#hash"एलिमेंट मार्कअप के साथ स्क्रीन रीडर की सुविधाprefers-reduced-motion, ताकि क्रॉसफ़ेड ट्रांज़िशन और इन-पेज तुरंत स्क्रोल करने की सुविधा चालू की जा सके- ड्राफ़्ट में मौजूद
@scroll-timelineवेब सुविधा, चुने गए टैब को डाइनैमिक तरीके से अंडरलाइन करने और उसका रंग बदलने के लिए है
एचटीएमएल
यहां उपयोगकर्ता अनुभव का मतलब है: किसी लिंक पर क्लिक करना, यूआरएल को नेस्ट किए गए पेज की स्थिति के तौर पर दिखाना, और फिर कॉन्टेंट एरिया को अपडेट होते हुए देखना. ऐसा तब होता है, जब ब्राउज़र मैच होने वाले एलिमेंट पर स्क्रोल करता है.
इसमें कुछ स्ट्रक्चरल कॉन्टेंट मेंबर होते हैं: लिंक और :target. हमें लिंक की एक सूची चाहिए. इसके लिए, <nav> का इस्तेमाल करना सबसे सही रहेगा. साथ ही, हमें <article> एलिमेंट की एक सूची चाहिए. इसके लिए, <section> का इस्तेमाल करना सबसे सही रहेगा. हर लिंक हैश, किसी सेक्शन से मैच करेगा. इससे ब्राउज़र, ऐंकरिंग की मदद से चीज़ों को स्क्रोल कर पाएगा.
उदाहरण के लिए, किसी लिंक पर क्लिक करने से, Chrome 89 में :target लेख पर अपने-आप फ़ोकस हो जाता है. इसके लिए, 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>
मैं href और id प्रॉपर्टी का इस्तेमाल करके, <a> और <article> एलिमेंट के बीच कनेक्शन बना सकता हूं. जैसे:
<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>
इसके बाद, मैंने लेखों में अलग-अलग लंबाई के lorem टेक्स्ट और लिंक जोड़े. साथ ही, मैंने अलग-अलग लंबाई के टाइटल और इमेज सेट जोड़े. कॉन्टेंट तैयार होने के बाद, हम लेआउट पर काम शुरू कर सकते हैं.
स्क्रोलिंग लेआउट
इस कॉम्पोनेंट में, स्क्रोल करने के लिए तीन अलग-अलग तरह के एरिया होते हैं:
- नेविगेशन (गुलाबी) को हॉरिज़ॉन्टल तरीके से स्क्रोल किया जा सकता है
- कॉन्टेंट एरिया (नीला) हॉरिज़ॉन्टल तौर पर स्क्रोल किया जा सकता है
- हर लेख आइटम (हरा) को वर्टिकल तौर पर स्क्रोल किया जा सकता है.
स्क्रोल करने के लिए, दो अलग-अलग तरह के एलिमेंट इस्तेमाल किए जाते हैं:
- विंडो
तय किए गए डाइमेंशन वाला एक बॉक्स, जिसमेंoverflowप्रॉपर्टी स्टाइल है. - ज़रूरत से ज़्यादा बड़ी स्क्रीन
इस लेआउट में, सूची के कंटेनर शामिल हैं: नेविगेशन लिंक, सेक्शन के लेख, और लेख का कॉन्टेंट.
<snap-tabs> लेआउट
मैंने टॉप लेवल लेआउट के तौर पर, फ़्लेक्स (फ़्लेक्सबॉक्स) को चुना. मैंने दिशा को 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; } }
रंगीन और तीन स्क्रोल वाले डायग्राम पर वापस ले जाने वाला ऐरो:
<header>अब (गुलाबी) स्क्रोल कंटेनर बनने के लिए तैयार है.<section>को (नीले) स्क्रोल कंटेनर के तौर पर तैयार किया गया है.
मैंने नीचे जिन फ़्रेम को VisBug से हाइलाइट किया है वे हमें स्क्रोल कंटेनर से बनाई गई विंडो देखने में मदद करते हैं.
टैब <header> लेआउट
अगला लेआउट भी करीब-करीब ऐसा ही है: मैं वर्टिकल ऑर्डर बनाने के लिए, फ़्लेक्स का इस्तेमाल करता हूं.
<snap-tabs>
<header>
<nav></nav>
<span class="snap-indicator"></span>
</header>
<section></section>
</snap-tabs>header { display: flex; flex-direction: column; }
.snap-indicator को लिंक के ग्रुप के साथ हॉरिज़ॉन्टल तौर पर स्क्रोल करना चाहिए. यह हेडर लेआउट, इस स्टेज को सेट करने में मदद करता है. यहां कोई भी ऐब्सलूट पोज़िशन वाला एलिमेंट नहीं है!
इसके बाद, स्क्रोल स्टाइल. इससे पता चलता है कि हम स्क्रोल स्टाइल को अपने दो हॉरिज़ॉन्टल स्क्रोल एरिया (हेडर और सेक्शन) के बीच शेयर कर सकते हैं. इसलिए, मैंने एक यूटिलिटी क्लास, .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> लेआउट
नेविगेशन लिंक को एक लाइन में लेआउट किया जाना चाहिए. इनमें कोई लाइन ब्रेक नहीं होना चाहिए और ये वर्टिकल तौर पर बीच में होने चाहिए. साथ ही, हर लिंक आइटम को स्क्रोल-स्नैप कंटेनर में स्नैप करना चाहिए. साल 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; } }
हर लिंक की स्टाइल और साइज़ अपने-आप तय होता है. इसलिए, नेविगेशन लेआउट में सिर्फ़ दिशा और फ़्लो तय करना होता है. नेविगेशन आइटम की अलग-अलग चौड़ाई की वजह से, टैब के बीच स्विच करना मज़ेदार हो जाता है. ऐसा इसलिए, क्योंकि इंडिकेटर अपनी चौड़ाई को नए टारगेट के हिसाब से अडजस्ट करता है. इसमें कितने एलिमेंट हैं, इसके आधार पर ब्राउज़र स्क्रोलबार रेंडर करेगा या नहीं.
टैब <section> लेआउट
यह सेक्शन एक फ़्लेक्स आइटम है और इसे स्पेस का मुख्य उपभोक्ता होना चाहिए. साथ ही, लेखों को रखने के लिए कॉलम भी बनाने होंगे. एक बार फिर, सीएसएस 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 स्टाइल, ग्रिड को बताता है कि वह चिल्ड्रन को हमेशा हॉरिज़ॉन्टल लाइन में लेआउट करे. इसमें कोई रैपिंग नहीं होनी चाहिए. हमें यही चाहिए, ताकि पैरंट विंडो ओवरफ़्लो हो सके.
कभी-कभी मुझे इन्हें समझने में मुश्किल होती है! यह सेक्शन एलिमेंट, एक बॉक्स में फ़िट हो रहा है. साथ ही, इसने बॉक्स का एक सेट भी बनाया है. उम्मीद है कि विज़ुअल और एक्सप्लेनेशन से आपको मदद मिल रही होगी.
टैब <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; }
मैंने लेखों को उनके पैरंट स्क्रोलर में स्नैप करने का विकल्प चुना. मुझे यह बहुत पसंद आया कि नेविगेशन लिंक आइटम और लेख के एलिमेंट, अपने-अपने स्क्रोल कंटेनर के इनलाइन-स्टार्ट पर कैसे स्नैप होते हैं. यह एक अच्छे रिश्ते की तरह दिखता है और महसूस होता है.
लेख, ग्रिड चाइल्ड है और इसका साइज़ पहले से तय होता है. यह व्यूपोर्ट का वह हिस्सा होता है जहां हमें स्क्रोल करने का उपयोगकर्ता अनुभव देना होता है. इसका मतलब है कि मुझे यहां लंबाई या चौड़ाई के लिए किसी स्टाइल की ज़रूरत नहीं है. मुझे सिर्फ़ यह तय करना है कि यह कैसे ओवरफ़्लो हो. मैंने overflow-y को auto पर सेट किया है. साथ ही, स्क्रोल इंटरैक्शन को ट्रैप करने के लिए, overscroll-behavior प्रॉपर्टी का इस्तेमाल किया है.
स्क्रोल किए जा सकने वाले तीन सेक्शन का रीकैप
नीचे, मैंने सिस्टम की सेटिंग में "हमेशा स्क्रोलबार दिखाएं" विकल्प चुना है. मुझे लगता है कि इस सेटिंग के चालू होने पर, लेआउट का काम करना बहुत ज़रूरी है. ऐसा इसलिए, क्योंकि मुझे लेआउट और स्क्रोल ऑर्केस्ट्रेशन की समीक्षा करनी है.
मुझे लगता है कि इस कॉम्पोनेंट में स्क्रोलबार गटर को देखने से, यह साफ़ तौर पर पता चलता है कि स्क्रोल करने की जगहें कहां हैं, वे किस दिशा में काम करती हैं, और वे एक-दूसरे के साथ कैसे इंटरैक्ट करती हैं. यह भी ध्यान रखें कि इनमें से हर स्क्रोल विंडो फ़्रेम, किसी लेआउट के लिए फ़्लेक्स या ग्रिड पैरंट भी होते हैं.
DevTools की मदद से, इसे विज़ुअलाइज़ किया जा सकता है:
स्क्रोल लेआउट की सुविधा पूरी तरह से उपलब्ध है: स्नैपिंग, डीप लिंक करने लायक, और कीबोर्ड से ऐक्सेस करने लायक. UX को बेहतर बनाने, स्टाइल, और आनंद के लिए मज़बूत बुनियाद.
सुविधा से जुड़ी खास बातें
साइज़ बदलने के दौरान, स्क्रोल स्नैप किए गए चाइल्ड एलिमेंट अपनी लॉक की गई पोज़िशन बनाए रखते हैं. इसका मतलब है कि डिवाइस को घुमाने या ब्राउज़र का साइज़ बदलने पर, JavaScript को कुछ भी दिखाने की ज़रूरत नहीं होगी. इसे Chromium DevTools के डिवाइस मोड में आज़माएं. इसके लिए, रिस्पॉन्सिव के अलावा कोई अन्य मोड चुनें. इसके बाद, डिवाइस के फ़्रेम का साइज़ बदलें. ध्यान दें कि एलिमेंट, व्यू में बना रहता है और अपने कॉन्टेंट के साथ लॉक रहता है. यह सुविधा तब से उपलब्ध है, जब Chromium ने स्पेसिफ़िकेशन के मुताबिक अपने काम करने के तरीके को अपडेट किया था. इसके बारे में ज़्यादा जानने के लिए, यह ब्लॉग पोस्ट पढ़ें.
ऐनिमेशन
यहां ऐनिमेशन का मकसद, इंटरैक्शन को यूज़र इंटरफ़ेस (यूआई) के फ़ीडबैक से साफ़ तौर पर जोड़ना है. इससे उपयोगकर्ता को कॉन्टेंट खोजने में मदद मिलती है. उम्मीद है कि इससे उपयोगकर्ता को कॉन्टेंट खोजने में कोई परेशानी नहीं होगी. मैं मोशन को सोच-समझकर और ज़रूरत के हिसाब से जोड़ूंगी. उपयोगकर्ता अब अपने ऑपरेटिंग सिस्टम में, मोशन से जुड़ी अपनी प्राथमिकताएं तय कर सकते हैं. मुझे अपने इंटरफ़ेस में उनकी प्राथमिकताओं के हिसाब से जवाब देना बहुत पसंद है.
मैं लेख के स्क्रोल की पोज़िशन के साथ, टैब के अंडरलाइन को लिंक करूंगा. स्नैपिंग सिर्फ़ अलाइनमेंट को बेहतर बनाने के लिए नहीं है, बल्कि यह ऐनिमेशन की शुरुआत और आखिर को ऐंकर करने के लिए भी है.
इससे <nav>, कॉन्टेंट से जुड़ा रहता है. यह मिनी-मैप की तरह काम करता है.
हम सीएसएस और जेएस, दोनों से उपयोगकर्ता की मोशन की सेटिंग की जांच करेंगे. कुछ टूल और साइटों की मदद से,
स्क्रोल व्यवहार
: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 से बदल देता हूं. टैब इंटरैक्शन में यह भी ध्यान दें कि ऐक्टिव नेविगेशन आइटम में न सिर्फ़ ब्रैंड की अंडरलाइन हाइलाइट होती है, बल्कि उसके टेक्स्ट का रंग भी गहरा होता है. ऐक्टिव एलिमेंट में, टेक्स्ट के रंग का कंट्रास्ट ज़्यादा होता है और उसके नीचे चमकदार रंग की अंडरलाइट होती है.
सीएसएस की कुछ अतिरिक्त लाइनों से, किसी व्यक्ति को यह महसूस होगा कि हम उसकी मोशन से जुड़ी प्राथमिकताओं का ध्यान रख रहे हैं. हालाँकि, मुझे यह पसंद है।
@scroll-timeline
ऊपर दिए गए सेक्शन में, मैंने आपको दिखाया कि मैं कम मोशन वाले क्रॉसफ़ेड स्टाइल को कैसे मैनेज करता हूं. इस सेक्शन में, मैं आपको दिखाऊंगा कि मैंने इंडिकेटर और स्क्रोल एरिया को एक साथ कैसे लिंक किया. अब हम कुछ मज़ेदार एक्सपेरिमेंट करेंगे. मुझे उम्मीद है कि आप भी मेरी तरह ही उत्साहित हैं.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
सबसे पहले, मैं JavaScript की मदद से उपयोगकर्ता की मोशन की सेटिंग देखता हूं. अगर इसका नतीजा false है, तो इसका मतलब है कि उपयोगकर्ता को कम मोशन पसंद है. ऐसे में, हम स्क्रोल लिंक करने वाले किसी भी मोशन इफ़ेक्ट को नहीं चलाएंगे.
if (motionOK) {
// motion based animation code
}
यह लेख लिखे जाने के समय, @scroll-timeline के लिए ब्राउज़र सहायता उपलब्ध नहीं है. यह एक ड्राफ़्ट स्पेक है, जिसमें सिर्फ़ एक्सपेरिमेंट के तौर पर लागू की गई सुविधाएं शामिल हैं. हालांकि, इसमें एक पॉलीफ़िल है, जिसका इस्तेमाल मैं इस डेमो में कर रहा हूं.
ScrollTimeline
सीएसएस और JavaScript, दोनों से स्क्रोल टाइमलाइन बनाई जा सकती हैं. हालांकि, मैंने JavaScript को चुना, ताकि मैं ऐनिमेशन में लाइव एलिमेंट मेज़रमेंट का इस्तेमाल कर सकूं.
const sectionScrollTimeline = new ScrollTimeline({
scrollSource: tabsection, // snap-tabs > section
orientation: 'inline', // scroll in the direction letters flow
fill: 'both', // bi-directional linking
});
मुझे एक एलिमेंट को दूसरे एलिमेंट की स्क्रोल पोज़िशन के हिसाब से स्क्रोल करना है. इसके लिए, मैं ScrollTimeline बनाकर, स्क्रोल लिंक के ड्राइवर को तय करता हूं, जो कि scrollSource है.
आम तौर पर, वेब पर कोई ऐनिमेशन, ग्लोबल टाइम फ़्रेम टिक के हिसाब से चलता है. हालांकि, मेमोरी में मौजूद कस्टम sectionScrollTimeline की मदद से, मैं इसे बदल सकता हूं.
tabindicator.animate({
transform: ...,
width: ...,
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
ऐनिमेशन के कीफ़्रेम के बारे में बताने से पहले, मुझे लगता है कि यह बताना ज़रूरी है कि स्क्रोलिंग को फ़ॉलो करने वाला tabindicator, कस्टम टाइमलाइन के हिसाब से ऐनिमेट होगा. यह टाइमलाइन, हमारे सेक्शन का स्क्रोल है. इससे लिंक करने की प्रोसेस पूरी हो जाती है. हालांकि, इसमें आखिरी चीज़ मौजूद नहीं है. यह चीज़ है, ऐनिमेशन के लिए स्टेटफ़ुल पॉइंट. इन्हें कीफ़्रेम भी कहा जाता है.
डाइनैमिक कीफ़्रेम
@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 वैल्यू के तौर पर इस्तेमाल करती हो. इससे ऐनिमेशन के लिए, चार ट्रांसफ़ॉर्म कीफ़्रेम बनते हैं. चौड़ाई के लिए भी ऐसा ही किया जाता है. हर एक से पूछा जाता है कि उसकी डाइनैमिक चौड़ाई क्या है. इसके बाद, उसे कीफ़्रेम वैल्यू के तौर पर इस्तेमाल किया जाता है.
यहां मेरे फ़ॉन्ट और ब्राउज़र की प्राथमिकताओं के आधार पर, आउटपुट का उदाहरण दिया गया है:
TranslateX कीफ़्रेम:
[...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"]
इस रणनीति के बारे में खास जानकारी देने के लिए, टैब इंडिकेटर अब सेक्शन स्क्रोलर की स्क्रोल स्नैप पोज़िशन के आधार पर, चार कीफ़्रेम में ऐनिमेट होगा. स्नैप पॉइंट, हमारे कीफ़्रेम के बीच साफ़ तौर पर अंतर दिखाते हैं. साथ ही, इनसे ऐनिमेशन को सिंक करने में मदद मिलती है.
उपयोगकर्ता, इंटरैक्शन करके ऐनिमेशन को कंट्रोल करता है. इससे, इंडिकेटर की चौड़ाई और पोज़िशन एक सेक्शन से दूसरे सेक्शन में बदलती है. साथ ही, स्क्रोल करने पर यह पूरी तरह से ट्रैक होता है.
शायद आपने ध्यान न दिया हो, लेकिन मुझे इस बात पर गर्व है कि हाइलाइट किए गए नेविगेशन आइटम को चुनने पर, उसका रंग बदल जाता है.
जब हाइलाइट किए गए आइटम का कंट्रास्ट ज़्यादा होता है, तो न चुना गया हल्का ग्रे रंग और भी ज़्यादा पीछे दिखता है. टेक्स्ट के रंग को बदलना आम बात है. जैसे, कर्सर घुमाने पर और चुने जाने पर. हालांकि, स्क्रोल करने पर उस रंग को बदलना और अंडरलाइन इंडिकेटर के साथ सिंक करना, एक अलग ही लेवल है.
मैंने यह काम इस तरह किया:
tabnavitems.forEach(navitem => {
navitem.animate({
color: [...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
});
हर टैब के नेविगेशन लिंक को इस नए कलर ऐनिमेशन की ज़रूरत होती है. यह अंडरलाइन इंडिकेटर की तरह ही स्क्रोल टाइमलाइन को ट्रैक करता है. मैं पहले वाली टाइमलाइन का ही इस्तेमाल करता हूं: इसका काम स्क्रोल करने पर टिक दिखाना है, इसलिए हम उस टिक का इस्तेमाल किसी भी तरह के ऐनिमेशन में कर सकते हैं. जैसा कि मैंने पहले किया था, मैं लूप में चार कीफ़्रेम बनाता हूं और रंग दिखाता हूं.
[...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) रंग वाला कीफ़्रेम, लिंक को हाइलाइट करता है. इसके अलावा, यह टेक्स्ट का स्टैंडर्ड रंग होता है. नेस्टेड लूप की वजह से, यह काफ़ी आसान हो जाता है. ऐसा इसलिए, क्योंकि बाहरी लूप हर नेविगेशन आइटम होता है और अंदरूनी लूप हर नेविगेशन आइटम के निजी कीफ़्रेम होते हैं. मैं यह जांच करता हूं कि बाहरी लूप एलिमेंट, अंदरूनी लूप एलिमेंट जैसा ही है या नहीं. साथ ही, इसका इस्तेमाल करके यह पता लगाता हूं कि इसे कब चुना गया है.
मुझे इसे लिखने में बहुत मज़ा आया. बहुत ज़्यादा.
JavaScript से जुड़ी और भी बेहतर सुविधाएं
यह याद रखना ज़रूरी है कि यहां जो कुछ भी दिखाया जा रहा है वह JavaScript के बिना भी काम करता है. चलिए, अब देखते हैं कि JS उपलब्ध होने पर, हम इसे कैसे बेहतर बना सकते हैं.
डीप लिंक
डीप लिंक, मोबाइल से जुड़ा शब्द है. हालांकि, मुझे लगता है कि डीप लिंक का मकसद, टैब के ज़रिए पूरा होता है. इसकी मदद से, किसी टैब के कॉन्टेंट का यूआरएल सीधे तौर पर शेयर किया जा सकता है. ब्राउज़र, पेज पर मौजूद उस आईडी पर इन-पेज नेविगेट करेगा जो यूआरएल हैश से मैच करता है. मुझे पता चला कि इस 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);
};
यह मानते हुए कि स्क्रोल स्नैप हो गया है, स्क्रोल एरिया की चौड़ाई से मौजूदा स्क्रोल पोज़िशन को भाग देने पर, दशमलव के बजाय पूर्णांक मिलना चाहिए. इसके बाद, मैं इस इंडेक्स की मदद से, हमारे कैश मेमोरी से एक नेविगेशन आइटम को ऐक्सेस करने की कोशिश करता हूं. अगर मुझे कुछ मिलता है, तो मैं मैच को सेट ऐक्टिव के लिए भेजता हूं.
const setActiveTab = tabbtn => {
tabnav
.querySelector(':scope a[active]')
.removeAttribute('active');
tabbtn.setAttribute('active', '');
tabbtn.scrollIntoView();
};
ऐक्टिव टैब सेट करने के लिए, पहले से ऐक्टिव टैब को हटाना होगा. इसके बाद, आने वाले नेविगेशन आइटम को ऐक्टिव स्टेटस एट्रिब्यूट देना होगा. scrollIntoView() को कॉल करने पर, सीएसएस के साथ एक मज़ेदार इंटरैक्शन होता है, जिस पर ध्यान देना ज़रूरी है.
.scroll-snap-x {
overflow: auto hidden;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
हॉरिज़ॉन्टल स्क्रोल स्नैप यूटिलिटी सीएसएस में, हमने एक मीडिया क्वेरी को नेस्ट किया है. अगर उपयोगकर्ता को मोशन से कोई समस्या नहीं है, तो यह क्वेरी smooth स्क्रोलिंग लागू करती है. JavaScript, एलिमेंट को स्क्रोल करके व्यू में लाने के लिए, आसानी से कॉल कर सकता है. साथ ही, सीएसएस, यूएक्स को डिक्लेरेटिव तरीके से मैनेज कर सकता है.
कभी-कभी, ये दोनों मिलकर बहुत ही शानदार जोड़ी बनाते हैं.
नतीजा
अब जब आपको पता चल गया है कि मैंने यह कैसे किया, तो आप इसे कैसे करेंगे?! इससे कॉम्पोनेंट आर्किटेक्चर काफ़ी मज़ेदार हो जाता है! अपने पसंदीदा फ़्रेमवर्क में स्लॉट के साथ पहला वर्शन कौन बनाएगा? 🙂
आइए, वेब पर कॉन्टेंट बनाने के अलग-अलग तरीकों के बारे में जानें. Glitch बनाएं, अपना वर्शन मुझे ट्वीट करें, और मैं इसे नीचे दिए गए कम्यूनिटी रीमिक्स सेक्शन में जोड़ दूंगा.
कम्यूनिटी रीमिक्स
- @devnook, @rob_dodson, और @DasSurma का वेब कॉम्पोनेंट के बारे में लेख.
- @jhvanderschee, बटन के साथ: Codepen.