कस्टम स्क्रोलबार बहुत ही कम होते हैं और इसकी मुख्य वजह यह है कि स्क्रोलबार, वेब पर बाकी बचे उन बिट में से एक हैं जिन्हें स्टाइल नहीं किया जा सकता (तारीख चुनने वाला टूल). आप JavaScript का इस्तेमाल करके अपना खुद का वर्शन बना सकते हैं. हालांकि, यह महंगा, लो फ़िडेलिटी, और असामान्य लग सकता है. इस लेख में, हम कुछ गैर-परंपरागत सीएसएस मैट्रिक्स का इस्तेमाल करके, एक कस्टम स्क्रोलर बनाने के बारे में बताएंगे. इसे स्क्रोल करते समय किसी JavaScript की ज़रूरत नहीं होती. हम बस कुछ सेटअप कोड का इस्तेमाल करेंगे.
बहुत ज़्यादा शब्द हैं, पढ़ा नहीं गया
क्या आपको छोटी-छोटी चीज़ों में कोई दिलचस्पी नहीं है? आप चाहें, तो Nyan बिल्ली का डेमो देखें और लाइब्रेरी देखें? डेमो का कोड, GitHub के रेपो में देखा जा सकता है.
LAM;WRA (लंबा और गणितीय; फिर भी पढ़ेगा)
कुछ समय पहले, हमने एक पैरालैक्स स्क्रोलर बनाया था (क्या आपने वह लेख पढ़ा था? यह वाकई बहुत अच्छा है और अपना समय देना फ़ायदेमंद है!). सीएसएस 3D ट्रांसफ़ॉर्म का इस्तेमाल करके एलिमेंट को वापस पुश करने से, एलिमेंट हमारी असल स्क्रोलिंग स्पीड की तुलना में ज़्यादा स्क्रोल करते हैं.
Recap
आइए, एक बार फिर से बताते हैं कि पैरालैक्स स्क्रोलर कैसे काम करता था.
जैसा कि ऐनिमेशन में दिखाया गया है, हमने 3D स्पेस में एलिमेंट को "पीछे की ओर" धकेलकर Z ऐक्सिस पर पैरालैक्स इफ़ेक्ट हासिल किया. किसी दस्तावेज़ को स्क्रोल करना, Y ऐक्सिस की मदद से अनुवाद किया जाता है. इसलिए, अगर हम नीचे स्क्रोल करके 100 पिक्सल तक स्क्रोल करते हैं, तो हर एलिमेंट का अनुवाद 100 पिक्सल तक ऊपर होगा. यह सभी एलिमेंट पर लागू होता है, इनमें वे एलिमेंट भी शामिल हैं जो “पुराने” हैं. हालांकि, क्योंकि वे कैमरे से दूर होते हैं, इसलिए स्क्रीन पर उनकी निगरानी की हुई मूवमेंट 100 पिक्सल से कम होगी, जिससे अपनी पसंद का पैरालक्स इफ़ेक्ट मिलेगा.
बेशक, किसी एलीमेंट को स्पेस में वापस ले जाने से वह छोटा भी दिखाई देगा, जिसे हम एलीमेंट का बैक अप लेकर सही करते हैं. पैरालैक्स स्क्रोलर बनाते समय हमने इसके बारे में सटीक गणित का पता लगाया था. इसलिए, मैं इसमें पूरी जानकारी नहीं दोहराऊँगी.
पहला चरण: हम क्या करना चाहते हैं?
स्क्रोलबार. हम यही टूल बनाने वाले हैं. लेकिन क्या आपने कभी सोचा है कि वे क्या काम करते हैं? निश्चित रूप से मुझे नहीं पता था. स्क्रोलबार से यह पता चलता है कि कितना मौजूदा समय में उपलब्ध कॉन्टेंट दिख रहा है और आपने पाठकों के तौर पर कितना प्रोग्रेस किया है. नीचे की ओर स्क्रोल करने पर, स्क्रोलबार से पता चलेगा कि आखिर की ओर स्क्रोल किया जा रहा है. अगर पूरा कॉन्टेंट व्यूपोर्ट में फ़िट हो जाता है, तो स्क्रोलबार आम तौर पर छिपा होता है. अगर कॉन्टेंट की ऊंचाई, व्यूपोर्ट की लंबाई से दोगुनी है, तो स्क्रोलबार, व्यूपोर्ट की ऊंचाई का 1⁄2 हिस्सा भर देता है. व्यूपोर्ट की ऊंचाई का तीन गुना मुताबिक होने वाला कॉन्टेंट, स्क्रोलबार को व्यूपोर्ट वगैरह के 1⁄3 साइज़ में बदल देता है. आपको पैटर्न दिख रहा है. स्क्रोल करने के बजाय, स्क्रोलबार पर क्लिक करके उसे खींचकर साइट पर तेज़ी से जाया जा सकता है. यह ऐसी चीज़ों के लिए हैरान करने वाला व्यवहार है. आइए, एक बार में एक ही लड़ाई लड़ें.
चरण 1: वैल्यू को उलटा रखना
ठीक है, सीएसएस 3D में बदलाव करने की सुविधा की मदद से, हम एलिमेंट को स्क्रोल करने की स्पीड से धीमा कर सकते हैं. जैसा कि पैरालैक्स स्क्रोलिंग लेख में बताया गया है. क्या हम दिशा को भी उलट सकते हैं? अब हम ऐसा कर सकते हैं. ऐसा ही एक बेहतरीन, कस्टम स्क्रोलबार बनाने के लिए किया जाता है. यह कैसे काम करता है, यह समझने के लिए हमें सबसे पहले सीएसएस 3D की कुछ बुनियादी बातों को समझना होगा.
गणित के नज़रिए से किसी भी तरह का नज़रिया जानने के लिए, शायद आप एक जैसे निर्देशांक इस्तेमाल करेंगे. मैं इस बारे में ज़्यादा जानकारी नहीं दे पाएगा कि वे क्या हैं और ये क्यों काम करते हैं. हालांकि, इन्हें w नाम के एक अतिरिक्त चौथे निर्देशांक के साथ 3D निर्देशांक की तरह समझा जा सकता है. यह कोऑर्डिनेट 1 होना चाहिए. हालांकि, अगर आपको ऐंगल का डिस्टॉर्शन नहीं रखना है. हमें w के ब्यौरे के बारे में परेशान होने की ज़रूरत नहीं है, क्योंकि हम 1 के अलावा किसी और वैल्यू का इस्तेमाल नहीं करेंगे. इसलिए, अब से सभी पॉइंट 4-डाइमेंशन वाले वेक्टर [x, y, z, w=1] पर हैं. इस वजह से, मैट्रिक्स का साइज़ भी 4x4 होना चाहिए.
ऐसा तब होता है, जब आप यह देख सकते हैं कि सीएसएस, हुड के तहत एक ही तरह के निर्देशांक इस्तेमाल करती है. ऐसा तब होता है, जब आप matrix3d()
फ़ंक्शन का इस्तेमाल करके, ट्रांसफ़ॉर्म प्रॉपर्टी में अपने 4x4 के आव्यूह (मैट्रिक) तय करते हैं. matrix3d
में 16 आर्ग्युमेंट इस्तेमाल किए जाते हैं, क्योंकि मैट्रिक्स
4x4 है. इस तरह, एक के बाद एक कॉलम तय होता है. हम इस फ़ंक्शन का इस्तेमाल, मैन्युअल तरीके से रोटेशन, अनुवाद वगैरह
के बारे में बताने के लिए कर सकते हैं. हालांकि, इससे हमें सिर्फ़
उस डब्ल्यू को मैनेज करने में गड़बड़ी मिलती है!
matrix3d()
का इस्तेमाल करने से पहले, हमें 3D कॉन्टेक्स्ट की ज़रूरत है. ऐसा इसलिए, क्योंकि 3D कॉन्टेक्स्ट के बिना ऐंगल का कोई भी हिस्सा डिस्टॉर्शन नहीं होगा और न ही एक जैसा निर्देशांक की ज़रूरत होगी. 3D संदर्भ बनाने के लिए, हमें perspective
वाले एक कंटेनर की ज़रूरत होती है. साथ ही, इसके अंदर कुछ ऐसे एलिमेंट भी होते हैं जिन्हें हम नए बनाए गए 3D स्पेस में बदल सकते हैं. उदाहरण
के लिए:
![सीएसएस कोड का एक हिस्सा, जो सीएसएस के नज़रिए एट्रिब्यूट का इस्तेमाल करके, किसी div को डिस्टॉर्शन करता है.](https://developer.chrome.com/static/docs/css-ui/custom-scrollbar/image/a-piece-css-code-distor-1ed892aadfd7c.png?authuser=7&hl=hi)
पर्सपेक्टिव कंटेनर के अंदर मौजूद एलिमेंट को सीएसएस इंजन प्रोसेस करता है. इसके लिए, यह तरीका अपनाएं:
- पर्सपेक्टिव कंटेनर के हिसाब से, एलिमेंट के हर कोने (वर्टेक्स) को समांगी निर्देशांक
[x,y,z,w]
में बदलें. - एलिमेंट के सभी रूपांतरण को दाएं से बाएं तक आव्यूह के रूप में लागू करें.
- अगर पर्सपेक्टिव एलिमेंट को स्क्रोल किया जा सकता है, तो स्क्रोल मैट्रिक्स लागू करें.
- पर्सपेक्टिव मैट्रिक्स लागू करें.
स्क्रोल मैट्रिक्स y ऐक्सिस के साथ किया गया एक अनुवाद है. अगर हम 400 पिक्सल तक नीचे की ओर स्क्रोल करते हैं, तो सभी एलिमेंट को 400 पिक्सल ऊपर ले जाना होगा. पर्सपेक्टिव मैट्रिक्स एक तरह का मैट्रिक्स है जो 3D स्पेस में गायब पॉइंट के और "लुप्त होने वाले पॉइंट" के और पास "खिसक जाता है". इससे पीछे की ओर चीज़ों को छोटा दिखाने के दोनों असर होते हैं. साथ ही, अनुवाद किए जाने पर चीज़ों की रफ़्तार "धीमी" होती है. इसलिए, अगर किसी एलिमेंट को पीछे भेजा जाता है, तो 400 पिक्सल के अनुवाद से एलिमेंट, स्क्रीन पर सिर्फ़ 300 पिक्सल मूव करेगा.
अगर आपको पूरी जानकारी चाहिए, तो आपको सीएसएस के ट्रांसफ़ॉर्म रेंडरिंग मॉडल की spec को पढ़ना चाहिए. हालांकि, इस लेख को ध्यान में रखते हुए, हमने ऊपर दिए गए एल्गोरिदम को आसान बना दिया है.
हमारा बॉक्स एक ऐसे पर्सपेक्टिव कंटेनर के अंदर है जिसमें 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);
}
ध्यान दें, हमारा बॉक्स वापस आ गया है!
चरण 2: उसे एक जगह से दूसरी जगह ले जाना
अब हमारा बॉक्स वहां है और वह वैसा ही दिख रहा है जैसा किसी बदलाव के बिना होता. इस समय, पर्सपेक्टिव कंटेनर को स्क्रोल नहीं किया जा सकता है. इसलिए, हम इसे नहीं देख सकते. हालांकि, हम जानते हैं कि स्क्रोल करने पर हमारा एलिमेंट दूसरी दिशा पर जाएगा. चलिए, कंटेनर को स्क्रोल करने के लिए कहते हैं, क्या हम? हम सिर्फ़ एक स्पेसर एलिमेंट जोड़ सकते हैं जो जगह लेता है:
<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 ऐक्सिस पर अपने अंगूठे का अनुवाद करने की कितनी ज़रूरत है. हालाँकि, ध्यान रखें कि हमारे डब्लू-कोऑर्डिनेट शनैनिगन की वजह से, हमें 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
पर भरोसा किया.
हम यहां भी यही काम करेंगे. अपनी याददाश्त को ताज़ा करने के लिए पैरालेसिंग लेख
पर एक नज़र डालें.
ब्राउज़र का स्क्रोलबार कैसा होता है?
कुछ सिस्टम में, हमें स्थायी, नेटिव स्क्रोलबार का इस्तेमाल करना होगा.
अब तक, स्क्रोलबार को छिपाया नहीं जा सकता. हालांकि, नॉन-स्टैंडर्ड pseudo-selector को ही इसमें शामिल किया जा सकता है.
इसलिए, इसे छिपाने के लिए हमें कुछ (गणित के बिना) हैकरी का सहारा लेना पड़ता है. हम अपने स्क्रोलिंग एलिमेंट को overflow-x: hidden
वाले कंटेनर में रैप करते हैं और स्क्रोलिंग एलिमेंट को कंटेनर से ज़्यादा चौड़ा बना देते हैं. ब्राउज़र का नेटिव स्क्रोलबार
अब व्यू में नहीं है.
फ़िन
इन सभी बातों को ध्यान में रखते हुए, अब हम फ़्रेम के हिसाब से एक कस्टम स्क्रोलबार बना सकते हैं. जैसा कि हमने Nyan cat डेमो में दिखाया है.
अगर आपको न्यान बिल्ली नहीं दिख रही है, तो इसका मतलब है कि आपको एक ऐसा बग मिला है, जो हमने इस डेमो को बनाते समय फ़ाइल किया था (न्यान बिल्ली को दिखाने के लिए अंगूठे पर क्लिक करें). Chrome, ग़ैर-ज़रूरी कामों से बचने में बहुत अच्छा है. जैसे, पेंटिंग या ऐनिमेशन वाले काम. बुरी खबर यह है कि हमारे मैट्रिक्स बदलावों से Chrome को ऐसा लगता है कि न्यान कैट gif असल में स्क्रीन पर नहीं है. उम्मीद है कि यह समस्या जल्द ही ठीक हो जाएगी.
यह हो गया. बहुत काम की ज़रूरत थी. पूरा कॉन्टेंट पढ़ने के लिए आपका शुक्रिया. यह काम करने की कुछ असली कोशिश है और यह शायद कभी-कभार ही काम की हो. बस अपनी पसंद के मुताबिक बनाया गया स्क्रोलबार बनाना ही इस अनुभव का ज़रूरी हिस्सा हो. लेकिन यह जानकर अच्छा लगा कि ऐसा करना मुमकिन है, नहीं? एक कस्टम स्क्रोलबार बनाना इतना मुश्किल है कि सीएसएस की ओर से काम किया जा रहा है. लेकिन डरें नहीं! आने वाले समय में, Huudini का AnimationWorklet फ़्रेमवर्क, स्क्रोल से लिंक किए गए इस तरह के बेहतरीन इफ़ेक्ट को और आसान बना देगा.