कस्टम स्क्रोलबार बहुत कम मिलते हैं. इसकी मुख्य वजह यह है कि वेब पर स्क्रोलबार, उन चीज़ों में से एक है जिनमें स्टाइल लागू नहीं की जा सकती. जैसे, तारीख चुनने वाला टूल. हालांकि, JavaScript का इस्तेमाल करके खुद का मॉडल बनाया जा सकता है, लेकिन यह महंगा होता है और इसमें कम फ़िडेलिटी होती है. साथ ही, यह धीमा भी हो सकता है. इस लेख में, हम कुछ अनोखी सीएसएस मेट्रिक का इस्तेमाल करके, एक कस्टम स्क्रोलर बनाएंगे. इस स्क्रोलर को स्क्रोल करते समय, JavaScript की ज़रूरत नहीं पड़ेगी. इसके लिए, सिर्फ़ कुछ सेटअप कोड की ज़रूरत होगी.
कम शब्दों में कहा जाए तो
क्या आपको छोटी-छोटी बातों से फ़र्क़ नहीं पड़ता? क्या आपको सिर्फ़ Nyan cat का डेमो देखना है और लाइब्रेरी चाहिए? डेमो का कोड, GitHub के रेपो में देखा जा सकता है.
LAM;WRA (लंबा और गणितीय; फिर भी पढ़ा जाएगा)
कुछ समय पहले, हमने पैरलॅक्स स्क्रोलर बनाया था (क्या आपने वह लेख पढ़ा है? यह बहुत अच्छा है, इसे देखना ज़रूर चाहिए!). सीएसएस 3D ट्रांसफ़ॉर्म का इस्तेमाल करके एलिमेंट को वापस पुश करने से, एलिमेंट हमारी असल स्क्रोलिंग स्पीड की तुलना में ज़्यादा स्क्रोल करते हैं.
रीकैप
चलिए, पैरलॅक्स स्क्रोलर के काम करने के तरीके की खास जानकारी से शुरुआत करते हैं.
ऐनिमेशन में दिखाए गए तरीके से, हमने पैरलॅक्स इफ़ेक्ट हासिल किया. इसके लिए, हमने एलिमेंट को 3D स्पेस में Z ऐक्सिस के साथ “पीछे” धकेला. किसी दस्तावेज़ को स्क्रोल करना, Y ऐक्सिस की मदद से बेहतर अनुवाद की तरह होता है. इसलिए, अगर हम 100 पिक्सल नीचे की ओर स्क्रोल करते हैं, तो हर एलिमेंट 100 पिक्सल ऊपर की ओर ट्रांसलेट हो जाएगा. यह सभी एलिमेंट पर लागू होता है. यहां तक कि उन एलिमेंट पर भी जो “ज़्यादा पीछे” हैं. हालांकि, इसकी वजह यह है कि वे कैमरे से ज़्यादा दूर हैं. इसलिए, स्क्रीन पर उनकी गति 100 पिक्सल से कम होगी. इससे, पैरलॅक्स इफ़ेक्ट दिखेगा.
बेशक, किसी एलिमेंट को वापस स्पेस में ले जाने पर भी वह छोटा दिखेगा. हम एलिमेंट को फिर से स्केल करके इसे ठीक करते हैं. पैरालैक्स स्क्रोलर बनाते समय हमने इसके बारे में सटीक गणित का पता लगाया था. इसलिए, मैं इसमें पूरी जानकारी नहीं दोहराऊँगी.
पहला चरण: हमें क्या करना है?
स्क्रोलबार. हम ऐसा ही ऐप्लिकेशन बनाने जा रहे हैं. लेकिन क्या आपने कभी सोचा है कि ये लोग क्या करते हैं? मुझे नहीं पता था. स्क्रोलबार से पता चलता है कि मौजूदा कॉन्टेंट का कितना हिस्सा दिख रहा है और आपने कितना कॉन्टेंट पढ़ लिया है. नीचे की ओर स्क्रोल करने पर, स्क्रोलबार से पता चलेगा कि लहजे को आखिर में पूरा किया जा रहा है. अगर पूरा कॉन्टेंट व्यूपोर्ट में फ़िट हो जाता है, तो स्क्रोलबार आम तौर पर छिपा रहता है. अगर कॉन्टेंट की ऊंचाई, व्यूपोर्ट की ऊंचाई से दो गुनी है, तो स्क्रोलबार, व्यूपोर्ट की आधी ऊंचाई तक भर जाता है. व्यूपोर्ट की ऊंचाई के 3 गुना के बराबर का कॉन्टेंट, स्क्रोलबार को व्यूपोर्ट वगैरह के 1⁄3 साइज़ में फ़िट कर देता है. आपको पैटर्न दिख रहा है. साइट पर तेज़ी से जाने के लिए, स्क्रोल करने के बजाय, स्क्रोलबार पर क्लिक करके उसे खींचकर छोड़ा जा सकता है. इस तरह के किसी सामान्य एलिमेंट के लिए, यह आश्चर्यजनक है कि इस तरह की गतिविधि हुई. आइए, एक बार में एक ही लड़ाई लड़ें.
पहला चरण: इसे रिवर्स में डालना
ठीक है, हम पैरलॅक्स स्क्रोलिंग वाले लेख में बताए गए तरीके से, सीएसएस 3D ट्रांसफ़ॉर्म की मदद से, एलिमेंट को स्क्रोलिंग स्पीड से धीमी गति से मूव कर सकते हैं. क्या हम दिशा को भी उलट सकते हैं? हमने पाया कि ऐसा किया जा सकता है. यही वजह है कि हम फ़्रेम के हिसाब से, कस्टम स्क्रोलबार बनाने के लिए इस तरीके का इस्तेमाल कर रहे हैं. यह समझने के लिए कि यह कैसे काम करता है, हमें पहले CSS 3D की कुछ बुनियादी बातों के बारे में बताना होगा.
गणित के हिसाब से किसी भी तरह का पर्सपेक्टिव प्रोजेक्शन पाने के लिए, आपको एक जैसे निर्देशांक का इस्तेमाल करना पड़ेगा. हम इस बारे में ज़्यादा जानकारी नहीं देंगे कि ये क्या हैं और ये कैसे काम करते हैं. हालांकि, इन्हें w नाम के चौथे निर्देशांक के साथ 3D निर्देशांक की तरह माना जा सकता है. अगर आपको पर्सपेक्टिव में बदलाव नहीं करना है, तो यह कोऑर्डिनेट 1 होना चाहिए. हमें w की जानकारी के बारे में चिंता करने की ज़रूरत नहीं है, क्योंकि हम 1 के अलावा किसी और वैल्यू का इस्तेमाल नहीं करने वाले हैं. इसलिए, अब से सभी पॉइंट 4-डाइमेंशन वाले वेक्टर [x, y, z, w=1] पर हैं. इस वजह से, मैट्रिक्स का साइज़ भी 4x4 होना चाहिए.
matrix3d()
फ़ंक्शन का इस्तेमाल करके, ट्रांसफ़ॉर्म प्रॉपर्टी में अपना 4x4 मैट्रिक्स तय करने पर, सीएसएस में होमोजेनिएस कोऑर्डिनेट का इस्तेमाल होता है. matrix3d
में 16 आर्ग्युमेंट होते हैं, क्योंकि मैट्रिक्स 4x4 होती है. इसमें एक के बाद एक कॉलम तय किए जाते हैं. इसलिए, इस फ़ंक्शन का इस्तेमाल करके, रोटेशन, ट्रांसलेशन वगैरह को मैन्युअल तरीके से तय किया जा सकता है. हालांकि, इसकी मदद से w कोऑर्डिनेट में भी बदलाव किया जा सकता है!
matrix3d()
का इस्तेमाल करने से पहले, हमें 3D संदर्भ की ज़रूरत होती है – क्योंकि 3D संदर्भ के बिना, पर्सपेक्टिव में कोई बदलाव नहीं होगा और एक जैसे निर्देशांक की ज़रूरत नहीं होगी. 3D कॉन्टेक्स्ट बनाने के लिए, हमें एक कंटेनर की ज़रूरत होती है. इसमें perspective
और कुछ ऐसे एलिमेंट होने चाहिए जिन्हें नए बनाए गए 3D स्पेस में ट्रांसफ़ॉर्म किया जा सके. उदाहरण के लिए:
पर्सपेक्टिव कंटेनर में मौजूद एलिमेंट को सीएसएस इंजन इस तरह प्रोसेस करता है:
- किसी एलिमेंट के हर कोने (वर्टिक्स) को, पर्सपेक्टिव कंटेनर के हिसाब से एक जैसे निर्देशांक
[x,y,z,w]
में बदलें. - एलिमेंट के सभी ट्रांसफ़ॉर्म को दाईं से बाईं ओर मैट्रिक्स के तौर पर लागू करें.
- अगर पर्सपेक्टिव एलिमेंट को स्क्रोल किया जा सकता है, तो स्क्रोल मैट्रिक लागू करें.
- पर्सपेक्टिव मैट्रिक्स लागू करें.
स्क्रोल मैट्रिक्स y ऐक्सिस के साथ किया गया एक अनुवाद है. अगर हम 400 पिक्सल तक स्क्रोल करें, तो सभी एलिमेंट को 400 पिक्सल तक ऊपर ले जाना होगा. पर्सपेक्टिव मैट्रिक एक ऐसा मैट्रिक है जो 3D स्पेस में पीछे की ओर मौजूद पॉइंट को, वैनिशिंग पॉइंट के ज़्यादा करीब “खींचता” है. इससे, चीज़ों को दूर होने पर छोटा दिखाने और अनुवाद करते समय उन्हें “धीमी गति से आगे बढ़ने” का दोनों ही असर मिलता है. इसलिए, अगर किसी एलिमेंट को पीछे भेजा जाता है, तो 400 पिक्सल के अनुवाद से एलिमेंट, स्क्रीन पर सिर्फ़ 300 पिक्सल मूव करेगा.
अगर आपको पूरी जानकारी चाहिए, तो आपको सीएसएस के ट्रांसफ़ॉर्म रेंडरिंग मॉडल के बारे में स्पेसिफ़िकेशन पढ़ना चाहिए. हालांकि, इस लेख के लिए, मैंने ऊपर दिए गए एल्गोरिदम को आसान बनाया है.
हमारा बॉक्स, perspective
एट्रिब्यूट की वैल्यू p वाले पर्सपेक्टिव कंटेनर के अंदर है. मान लें कि कंटेनर को स्क्रोल किया जा सकता है और इसे n पिक्सल नीचे स्क्रोल किया गया है.
पहला मैट्रिक, पर्सपेक्टिव मैट्रिक है और दूसरा मैट्रिक, स्क्रोल मैट्रिक है. रीकैप करने के लिए: स्क्रोल मैट्रिक का काम यह है कि जब हम नीचे की ओर स्क्रोल कर रहे हों, तब किसी एलिमेंट को ऊपर की ओर ले जाएं. इसलिए, इसमें नेगेटिव साइन का इस्तेमाल किया जाता है.
हालांकि, स्क्रोलबार के लिए हमें इसके उलट करना है – हमें नीचे स्क्रोल करने पर, अपने एलिमेंट को नीचे ले जाना है. यहां हम एक ट्रिक का इस्तेमाल कर सकते हैं:
अपने बॉक्स के कोनों के w निर्देशांक को उलटना. अगर w कोऑर्डिनेट का वैल्यू -1 है, तो सभी अनुवाद, विपरीत दिशा में लागू होंगे. तो हम यह कैसे करें? सीएसएस इंजन, बॉक्स के कोनों को समान निर्देशांक में बदलता है. साथ ही, w को 1 पर सेट करता है. अब matrix3d()
का समय आ गया है!
.box {
transform:
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
);
}
यह मैट्रिक, w को नेगेटिव करने के अलावा कुछ नहीं करेगी. इसलिए, जब सीएसएस इंजन हर कोने को [x,y,z,1]
फ़ॉर्म के वेक्टर में बदल देता है, तो मैट्रिक उसे [x,y,z,-1]
में बदल देगी.
मैंने एलिमेंट ट्रांसफ़ॉर्म मैट्रिक का असर दिखाने के लिए, बीच में एक चरण जोड़ा है. अगर आपको मैट्रिक के हिसाब से गणित का इस्तेमाल करने में परेशानी होती है, तो कोई बात नहीं. अहम बात यह है कि आखिरी पंक्ति में, हम स्क्रोल ऑफ़सेट n को अपने y कोऑर्डिनेट में घटाने के बजाय जोड़ते हैं. अगर हम नीचे स्क्रोल करेंगे, तो एलिमेंट का अनुवाद नीचे होगा.
हालांकि, अगर हम इस मैट्रिक को सिर्फ़ अपने उदाहरण में डालते हैं, तो एलिमेंट नहीं दिखेगा. ऐसा इसलिए है, क्योंकि सीएसएस स्पेसिफ़िकेशन के मुताबिक, w < 0 वाला कोई भी वर्टिक्स, एलिमेंट को रेंडर होने से रोकता है. फ़िलहाल, हमारा z कोऑर्डिनेट 0 है और p 1 है, इसलिए w -1 होगा.
सौभाग्य से, हम z की वैल्यू चुन सकते हैं! यह पक्का करने के लिए कि हमें w=1 मिले, हमें z = -2 सेट करना होगा.
.box {
transform:
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
)
translateZ(-2px);
}
देखिए, हमारा बॉक्स वापस आ गया है!
दूसरा चरण: आइटम को मूव करना
अब हमारा बॉक्स दिख रहा है और यह वैसा ही दिख रहा है जैसा कि बिना किसी ट्रांसफ़ॉर्म के दिखता है. फ़िलहाल, पर्सपेक्टिव कंटेनर को स्क्रोल नहीं किया जा सकता. इसलिए, हम इसे नहीं देख सकते. हालांकि, हमें पता है कि स्क्रोल करने पर, हमारा एलिमेंट दूसरी दिशा में जाएगा. चलिए, कंटेनर को स्क्रोल करने के लिए कहते हैं, क्या हम? हम सिर्फ़ एक ऐसा स्पेस एलिमेंट जोड़ सकते हैं जो जगह लेता है:
<div class="container">
<div class="box"></div>
<span class="spacer"></span>
</div>
<style>
/* … all the styles from the previous example … */
.container {
overflow: scroll;
}
.spacer {
display: block;
height: 500px;
}
</style>
अब बॉक्स को स्क्रोल करें! लाल बॉक्स नीचे की ओर खिसक जाता है.
तीसरा चरण: उसे साइज़ दें
हमारे पास एक एलिमेंट है, जो पेज को नीचे की ओर स्क्रोल करने पर नीचे की ओर जाता है. अब यह काम हो गया. अब हमें इसे स्क्रोलबार की तरह दिखाने के लिए स्टाइल करना होगा और इसे थोड़ा ज़्यादा इंटरैक्टिव बनाना होगा.
आम तौर पर, स्क्रोलबार में “थंब” और “ट्रैक” होता है, जबकि ट्रैक हमेशा नहीं दिखता. थंबनेल की ऊंचाई, कॉन्टेंट के दिखने के हिसाब से तय होती है.
<script>
const scroller = document.querySelector('.container');
const thumb = document.querySelector('.box');
const scrollerHeight = scroller.getBoundingClientRect().height;
thumb.style.height = /* ??? */;
</script>
scrollerHeight
, स्क्रोल किए जा सकने वाले एलिमेंट की ऊंचाई है, जबकि
scroller.scrollHeight
, स्क्रोल किए जा सकने वाले कॉन्टेंट की कुल ऊंचाई है.
scrollerHeight/scroller.scrollHeight
, कॉन्टेंट के दिखने वाले हिस्से का हिस्सा है. थंबनेल में वर्टिकल स्पेस का अनुपात, दिखने वाले कॉन्टेंट के अनुपात के बराबर होना चाहिए:
<script>
// …
thumb.style.height =
scrollerHeight * scrollerHeight / scroller.scrollHeight + 'px';
// Accommodate for native scrollbars
thumb.style.right =
(scroller.clientWidth - scroller.getBoundingClientRect().width) + 'px';
</script>
थंबनेल का साइज़ अच्छा लग रहा है, लेकिन यह बहुत तेज़ी से चल रहा है. यहां हम पैरलॅक्स स्क्रोलर से अपनी तकनीक हासिल कर सकते हैं. अगर हम एलिमेंट को और पीछे ले जाते हैं, तो स्क्रोल करते समय वह धीमी गति से चलेगा. हम साइज़ को बड़ा करके, उसे ठीक कर सकते हैं. लेकिन, इसे वापस पाने के लिए हमें कितना करना चाहिए? चलिए, हिसाब लगाते हैं! वादा है, आखिरी बार.
अहम जानकारी यह है कि हम चाहते हैं कि पूरी तरह से स्क्रोल करने पर, अंगूठे के सबसे नीचे वाले हिस्से का अलाइनमेंट, स्क्रोल किए जा सकने वाले एलिमेंट के सबसे नीचे वाले हिस्से से हो. दूसरे शब्दों में: अगर हमने scroller.scrollHeight - scroller.height
पिक्सल स्क्रोल किए हैं, तो हमें अपने अंगूठे को scroller.height - thumb.height
तक ट्रांसलेट करना है. स्क्रोलर के हर एक पिक्सल के लिए, हम चाहते हैं कि हमारा अंगूठा पिक्सल के कुछ हिस्से को मूव करे:
यही हमारा स्केलिंग फ़ैक्टर है. अब हमें स्केलिंग फ़ैक्टर को z ऐक्सिस के साथ ट्रांसलेशन में बदलना होगा. हमने पैरलॅक्स स्क्रोलिंग वाले लेख में पहले ही ऐसा कर लिया है. स्पेसिफ़िकेशन के काम के सेक्शन के मुताबिक: स्केलिंग फ़ैक्टर, p/(p − z) के बराबर होता है. इस इक्वेशन को z के लिए हल करके, यह पता लगाया जा सकता है कि हमें z ऐक्सिस के साथ अपने अंगूठे को कितना ट्रांसलेट करना है. हालांकि, ध्यान रखें कि w निर्देशांक की वजह से, हमें z के साथ एक और -2px
का अनुवाद करना होगा. यह भी ध्यान रखें कि किसी एलिमेंट के ट्रांसफ़ॉर्म को दाएं से बाएं लागू किया जाता है. इसका मतलब यह है कि हमारे खास मैट्रिक्स से पहले के सभी अनुवाद उलटे नहीं होंगे. हालांकि, हमारे खास मैट्रिक्स के बाद किए गए सभी अनुवाद उल्टे नहीं होंगे! आइए, इसे कोड में बदलते हैं!
<script>
// ... code from above...
const factor =
(scrollerHeight - thumbHeight)/(scroller.scrollHeight - scrollerHeight);
thumb.style.transform = `
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
)
scale(${1/factor})
translateZ(${1 - 1/factor}px)
translateZ(-2px)
`;
</script>
हमारे पास एक स्क्रोलबार है! यह सिर्फ़ एक DOM एलिमेंट है. हम इसे अपनी पसंद के मुताबिक स्टाइल कर सकते हैं. सुलभता के लिहाज़ से, एक अहम बात यह है कि अंगूठे को क्लिक करके खींचने और छोड़ने की सुविधा जोड़ी जाए. ऐसा इसलिए, क्योंकि ज़्यादातर उपयोगकर्ता स्क्रोलबार के साथ इस तरह इंटरैक्ट करते हैं. इस ब्लॉग पोस्ट को और लंबा न बनाने के लिए, हम उस हिस्से के बारे में जानकारी नहीं देंगे. अगर आपको यह जानना है कि यह कैसे किया जाता है, तो ज़्यादा जानकारी के लिए लाइब्रेरी कोड देखें.
iOS के लिए क्या है?
अरे, मेरे पुराने दोस्त iOS Safari. पैरलॅक्स स्क्रोलिंग की तरह ही, हमें यहां भी एक समस्या आ रही है. हम किसी एलिमेंट पर स्क्रोल कर रहे हैं, इसलिए हमें -webkit-overflow-scrolling: touch
की जानकारी देनी होगी. हालांकि, इससे 3D फ़्लैट हो जाता है और हमारा पूरा स्क्रोलिंग इफ़ेक्ट काम करना बंद कर देता है. हमने पैरलॅक्स स्क्रोलर में इस समस्या को हल किया है. इसके लिए, हमने iOS Safari का पता लगाया और position: sticky
का इस्तेमाल किया. हम यहां भी यही तरीका अपनाएंगे. अपनी याददाश्त को रीफ़्रेश करने के लिए, पैरललक्स वाला लेख पढ़ें.
ब्राउज़र स्क्रोलबार के बारे में क्या?
कुछ सिस्टम पर, हमें हमेशा मौजूद नेटिव स्क्रोलबार का इस्तेमाल करना होगा.
अब तक, स्क्रोलबार को छिपाया नहीं जा सकता था. हालांकि, नॉन-स्टैंडर्ड स्यूडो-सिलेक्टर की मदद से ऐसा किया जा सकता है.
इसलिए, इसे छिपाने के लिए, हमें कुछ हैकिंग (बिना हिसाब-किताब के) का सहारा लेना होगा. हम अपने स्क्रोलिंग एलिमेंट को overflow-x: hidden
के साथ कंटेनर में रैप करते हैं और स्क्रोलिंग एलिमेंट को कंटेनर से ज़्यादा चौड़ा बनाते हैं. ब्राउज़र का नेटिव स्क्रोलबार अब दिखने बंद हो गया है.
फ़िन
इन सभी चीज़ों को एक साथ जोड़कर, अब हम फ़्रेम के हिसाब से पसंद के मुताबिक स्क्रोलबार बना सकते हैं. जैसे, Nyan cat के डेमो में दिखाया गया स्क्रोलबार.
अगर आपको Nyan cat नहीं दिख रहा है, तो इसका मतलब है कि आपको एक गड़बड़ी का सामना करना पड़ रहा है. हमने इस गड़बड़ी को ढूंढकर, उसे दर्ज कर लिया है. Nyan cat को दिखाने के लिए, थंब पर क्लिक करें. Chrome, ग़ैर-ज़रूरी कामों से बचने में काफ़ी अच्छा है. जैसे, स्क्रीन से बाहर की चीज़ों को पेंट करना या ऐनिमेशन देना. बुरी खबर यह है कि मैट्रिक्स की तरह की हमारी गतिविधियों की वजह से, Chrome को लगता है कि न्यान कैट का GIF फ़ाइल, स्क्रीन पर नहीं है. उम्मीद है कि यह समस्या जल्द ही ठीक हो जाएगी.
बस हो गया. इसमें काफ़ी काम था. पूरा कॉन्टेंट पढ़ने के लिए आपका शुक्रिया. इसे काम कराने के लिए, कुछ असली तरकीबें अपनानी पड़ती हैं. ऐसा शायद ही कभी किया जाना चाहिए. हालांकि, अगर उपयोगकर्ताओं को पसंद के मुताबिक स्क्रोलबार देना ज़रूरी है, तो ऐसा किया जा सकता है. हालांकि, यह जानकर अच्छा लगा कि ऐसा किया जा सकता है, है न? कस्टम स्क्रोलबार बनाने में इतनी मुश्किल आना, इस बात का सबूत है कि सीएसएस को इस पर काम करना होगा. लेकिन घबराएं नहीं! आने वाले समय में, Houdini के AnimationWorklet की मदद से, स्क्रीन पर स्क्रोल करने पर दिखने वाले इस तरह के इफ़ेक्ट को आसानी से बनाया जा सकेगा.