परफ़ॉर्मेंट पैरालैक्सिंग

चाहे आप इसे पसंद करें या फिर इससे नफ़रत करें, पैरालक्सिंग का आनंद लिया जा सकता है. जब इसका इस्तेमाल सोच-समझकर किया जाता है, तो इससे वेब ऐप्लिकेशन की बारीकियां और बारीकियां दिखती हैं. हालांकि, समस्या यह है कि बेहतर परफ़ॉर्म करने वाले तरीके से पैरालक्सिंग करना चुनौती भरा हो सकता है. इस लेख में, हम ऐसे समाधान के बारे में बात करेंगे जो बेहतर परफ़ॉर्म करने वाला होने के साथ-साथ, क्रॉस-ब्राउज़र काम भी करता है.

पैरालैक्स का इलस्ट्रेशन.

बहुत ज़्यादा शब्द हैं, पढ़ा नहीं गया

  • पैरालक्स ऐनिमेशन बनाने के लिए, स्क्रोल इवेंट या background-position का इस्तेमाल न करें.
  • ज़्यादा सटीक पैरालैक्स इफ़ेक्ट बनाने के लिए, सीएसएस 3D ट्रांसफ़ॉर्म का इस्तेमाल करें.
  • मोबाइल Safari के लिए, position: sticky का इस्तेमाल करके यह पक्का करें कि पैरालैक्स इफ़ेक्ट दिखने लगे.

अगर आपको ड्रॉप-इन सलूशन चाहिए, तो यूज़र इंटरफ़ेस (यूआई) एलिमेंट के सैंपल GitHub रेपो पर जाएं और Parallax हेल्पर JS पाएं! GitHub रेपो में, पैरलैक्स स्क्रोलर का लाइव डेमो देखा जा सकता है.

सवाल हल करने वाले पैरालक्सर

सबसे पहले, हम पैरालैक्स इफ़ेक्ट को हासिल करने के दो आम तरीकों पर नज़र डालते हैं. खास तौर पर, यह जानें कि वे हमारे मकसद के लिए सही क्यों नहीं हैं.

खराब: स्क्रोल इवेंट का इस्तेमाल किया जा रहा है

पैरालैक्सिंग के लिए ज़रूरी है कि इसे स्क्रोल-कपल्ड में रखा जाए. पेज की स्क्रोल पोज़िशन में होने वाले हर बदलाव के लिए, पैरालैक्सिंग एलिमेंट की पोज़िशन अपडेट होनी चाहिए. यह आसान लगता है, लेकिन आधुनिक ब्राउज़र का एक अहम तरीका यह है कि वे एसिंक्रोनस रूप से काम कर सकें. यह हमारे खास मामले में इवेंट को स्क्रोल करने पर लागू होता है. ज़्यादातर ब्राउज़र में, स्क्रोल इवेंट को "बेहतर कोशिश" के तौर पर दिखाया जाता है. साथ ही, इस बात की कोई गारंटी नहीं है कि इन्हें स्क्रोल ऐनिमेशन के हर फ़्रेम पर डिलीवर किया जाएगा!

यह ज़रूरी जानकारी हमें बताती है कि हमें JavaScript पर आधारित ऐसे समाधान का इस्तेमाल क्यों नहीं करना चाहिए जो स्क्रोल इवेंट के आधार पर एलिमेंट को दूसरी जगह ले जाता है: JavaScript इस बात की गारंटी नहीं देता कि पैरालैक्सिंग, पेज की स्क्रोल पोज़िशन के हिसाब से काम करेगा. Mobile Safari के पुराने वर्शन में, स्क्रोल इवेंट के आखिर में स्क्रोल इवेंट दिए जाते थे. इस वजह से, JavaScript पर आधारित स्क्रोल इफ़ेक्ट बनाना नामुमकिन हो गया. हाल के वर्शन ऐनिमेशन के दौरान स्क्रोल इवेंट देते हैं, लेकिन ये Chrome की तरह ही "बेहतर कोशिश" के आधार पर डिलीवर किए जाते हैं. अगर मुख्य थ्रेड किसी दूसरे काम में व्यस्त है, तो स्क्रोल इवेंट तुरंत डिलीवर नहीं होंगे. इसका मतलब है कि पैरालैक्स इफ़ेक्ट हट जाएगा.

खराब: background-position को अपडेट किया जा रहा है

हम दूसरी स्थिति से बचना चाहते हैं, वह है हर फ़्रेम पर पेंटिंग बनाना. कई समाधान background-position को बदलकर पैरालक्स लुक देने की कोशिश करते हैं. इस वजह से ब्राउज़र, स्क्रोल करने पर पेज के प्रभावित हिस्सों को फिर से पेंट करता है. इनकी वजह से ऐनिमेशन को काफ़ी ज़्यादा खर्च करना पड़ सकता है.

अगर हम पैरालैक्स मोशन के प्रॉमिस को पूरा करना चाहते हैं, तो हम कुछ ऐसा चाहते हैं जिसे एक एक्सेलरेटेड प्रॉपर्टी के रूप में लागू किया जा सके (इसका मतलब है ट्रांसफ़ॉर्म और ओपैसिटी के तौर पर बने रहना) और यह स्क्रोल इवेंट पर निर्भर नहीं है.

3D में सीएसएस

स्कॉट कैलम और कीथ क्लार्क दोनों ने सीएसएस 3D का इस्तेमाल करके, पैरालक्स मोशन को पाने के लिए अहम काम किया है. साथ ही, वे जिस तकनीक का इस्तेमाल करते हैं वह इस तरह है:

  • overflow-y: scroll (और शायद overflow-x: hidden) के साथ स्क्रोल करने के लिए, एक कंटेनिंग एलिमेंट सेट अप करें.
  • उसी एलिमेंट के लिए, perspective वैल्यू और perspective-origin को top left या 0 0 पर सेट करें.
  • उस एलिमेंट के बच्चों के लिए Z में अनुवाद लागू करें और स्क्रीन पर उनके साइज़ पर असर डाले बिना, पैरालक्स मोशन देने के लिए उनका बैक अप लें.

इस तरीके के लिए सीएसएस ऐसा दिखता है:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

जो इस तरह के एचटीएमएल का स्निपेट मान लेता है:

<div class="container">
    <div class="parallax-child"></div>
</div>

नज़रिए के लिए स्केल एडजस्ट करना

चाइल्ड एलिमेंट को पीछे की ओर धकेलने से, पर्सपेक्टिव वैल्यू के हिसाब से उसका अनुपात कम हो जाएगा. इस इक्वेशन की मदद से यह पता लगाया जा सकता है कि इसे कितना स्केल करना है: (प्रतिनिधि - दूरी) / ऐंगल. शायद हम चाहते हैं कि पैरालैक्सिंग एलिमेंट भी उसी साइज़ में दिखे, जो हमने लिखा था. इसलिए, उसे इसी तरह से स्केल करने की ज़रूरत होगी, न कि उसी तरह से.

ऊपर दिए गए कोड के मामले में, ऐंगल 1 पिक्सल है और parallax-child के Z की दूरी -2 पिक्सल है. इसका मतलब है कि एलिमेंट को 3x तक स्केल करना होगा. आपको यह दिखेगा कि कोड में प्लग की गई वैल्यू, scale(3) है.

जिस कॉन्टेंट पर translateZ की वैल्यू लागू नहीं की गई है उसके लिए, वैल्यू को शून्य से बदला जा सकता है. इसका मतलब है कि स्केल (perspective - 0) / perspective है, जो 1 की वैल्यू पर आता है. इसका मतलब है कि स्केल को न तो बढ़ाया गया है और न ही कम किया गया है. काफ़ी आसान है.

यह तरीका कैसे काम करता है

यह समझना ज़रूरी है कि यह तरीका कारगर क्यों है, क्योंकि हम जल्द ही इस जानकारी का इस्तेमाल करने वाले हैं. स्क्रोल करना असल में एक ट्रांसफ़ॉर्म है. इसलिए, इसे फटाफट किया जा सकता है. इसमें ज़्यादातर लेयर को जीपीयू के हिसाब से शिफ़्ट करना पड़ता है. एक सामान्य स्क्रोल में, स्क्रोलिंग एलिमेंट और उसके बच्चों की तुलना करते समय, स्क्रोलिंग 1:1 तरीके से होती है. यह स्क्रोलिंग एलिमेंट के बारे में बताती है. अगर किसी एलिमेंट को 300px तक नीचे की ओर स्क्रोल किया जाता है, तो उसके चाइल्ड एलिमेंट को भी उतनी ही संख्या में कन्वर्ट किया जाता है: 300px.

हालांकि, स्क्रोलिंग एलिमेंट पर एक ऐंगल वैल्यू लागू करने से, इस प्रोसेस में गड़बड़ी हो जाती है. इससे, स्क्रोल ट्रांसफ़ॉर्म को कम करने वाले मैट्रिक्स बदल जाते हैं. अब 300 पिक्सल के स्क्रोल से, बच्चों को सिर्फ़ 150 पिक्सल तक आगे बढ़ाया जा सकता है. यह आपकी चुनी गई perspective और translateZ वैल्यू पर निर्भर करता है. अगर किसी एलिमेंट की translateZ वैल्यू 0 है, तो उसे 1:1 पर स्क्रोल किया जाएगा (जैसा कि यह पहले किया जाता था), लेकिन जिस बच्चे को नज़रिए के ऑरिजिन से Z दूर धकेल दिया जाता है उसे दूसरी दर पर स्क्रोल किया जाएगा! कुल नतीजा: पैरालैक्स गति. सबसे अहम बात यह है कि इसे ब्राउज़र की इंटरनल स्क्रोल मशीन के हिस्से के तौर पर अपने-आप मैनेज किया जाता है. इसका मतलब है कि scroll इवेंट को सुनने या background-position को बदलने की ज़रूरत नहीं होती.

ऑइंटमेंट में मक्खी: मोबाइल Safari

हर प्रभाव के लिए कुछ सीमाएं हैं और बदलाव लाने के लिए एक अहम बात यह है कि चाइल्ड एलिमेंट में 3D इफ़ेक्ट का रखरखाव किया जाए. अगर एलिमेंट और उसके पैरालैक्सिंग चाइल्ड एलिमेंट के बीच एलिमेंट के क्रम में एलिमेंट हैं, तो 3D ऐंगल "फ़्लैट किया गया" होता है.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

ऊपर दिए गए एचटीएमएल में, .parallax-container नया है और यह perspective वैल्यू को असरदार तरीके से फ़्लैट कर देगा और हम पैरालैक्स इफ़ेक्ट को खो देंगे. ज़्यादातर मामलों में, समाधान सीधे तौर पर आसान होता है: एलिमेंट में transform-style: preserve-3d जोड़ा जाता है, जिसकी वजह से कोई भी 3D इफ़ेक्ट (जैसे, हमारा नज़रिया) लागू होता है, जो पेड़ की सतह पर लागू किया गया है.

.parallax-container {
  transform-style: preserve-3d;
}

हालांकि, मोबाइल Safari के मामले में चीज़ें थोड़ी जटिल हो जाती हैं. कंटेनर एलिमेंट में overflow-y: scroll को लागू करने का तरीका तकनीकी तौर पर काम करता है. हालांकि, स्क्रोलिंग एलिमेंट को फ़्लिंग करने की लागत ज़्यादा होती है. इसका हल है -webkit-overflow-scrolling: touch को जोड़ना. हालांकि, इससे perspective फ़्लैट हो जाएगा और हमें पैरालैक्सिंग नहीं मिलेगी.

प्रोग्रेसिव एन्हैंसमेंट की सुविधा के हिसाब से, शायद यह कोई बड़ी समस्या न हो. अगर हम हर स्थिति में पैरालक्स नहीं कर पा रहे हैं, तो भी हमारा ऐप्लिकेशन काम करता रहेगा. हालांकि, इस समस्या का हल निकालना ही बेहतर रहेगा.

position: sticky ने बचाया!

असल में, position: sticky के रूप में कुछ मदद मिलती है, जो स्क्रोल के दौरान एलिमेंट को व्यूपोर्ट या किसी दिए गए पैरंट एलिमेंट पर "स्टिक" रखने की अनुमति देती है. इनमें से ज़्यादातर की तरह, विशेषता भी काफ़ी जटिल है, लेकिन इसमें एक छोटा सा जेम शामिल है:

पहली नज़र में इसका बहुत ज़्यादा मतलब नहीं हो सकता. हालांकि, इस वाक्य में खास बात यह है कि किसी एलिमेंट के स्टिकीनेस का हिसाब कैसे, सटीक तौर पर लगाया जाता है: "ऑफ़सेट की गिनती स्क्रोलिंग बॉक्स के साथ सबसे नज़दीकी एंसेस्टर के रेफ़रंस के साथ की जाती है". दूसरे शब्दों में, स्टिकी एलिमेंट को एक जगह से दूसरी जगह ले जाने की दूरी (किसी दूसरे एलिमेंट या व्यूपोर्ट से अटैच किए जाने के लिए) का हिसाब, किसी दूसरे बदलाव के लागू होने से पहले लगाया जाता है, बाद में नहीं. इसका मतलब है कि काफ़ी हद तक स्क्रोल करने वाले उदाहरण की तरह, अगर ऑफ़सेट का हिसाब 300 पिक्सल में किया गया था, तो उस 300 पिक्सल ऑफ़सेट वैल्यू को स्टिकी एलिमेंट पर लागू करने से पहले उसमें बदलाव करने के लिए, नज़रिए (या किसी दूसरे बदलाव) का इस्तेमाल करने का नया मौका है.

पैरालैक्सिंग एलिमेंट पर position: -webkit-sticky लागू करके, हम -webkit-overflow-scrolling: touch के समतल करने के असर को "उल्टी" कर सकते हैं. इससे यह पक्का होता है कि पैरालैक्सिंग एलिमेंट, स्क्रोलिंग बॉक्स के साथ सबसे नज़दीक के ऐनसेस्टर का रेफ़रंस देता है. इस मामले में, .container है. इसके बाद, पहले की तरह ही, .parallax-container में perspective वैल्यू को लागू किया जाता है. इससे, आपके दिए गए फ़ॉर्मूला के आधार पर तैयार किए गए स्क्रोल ऑफ़सेट में बदलाव होता है और यह पैरालैक्स इफ़ेक्ट बनाता है.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

यह Mobile Safari के लिए पैरालक्स इफ़ेक्ट को पहले जैसा कर देता है, जो कि हर तरह से अच्छी खबर है!

स्टिकी पोज़िशनिंग से जुड़ी सावधानियां

हालांकि, यहां अंतर है. position: sticky पैरालैक्स की तकनीक में बदलाव नहीं करता. स्टिकी पोज़िशनिंग की मदद से एलिमेंट को स्क्रोलिंग कंटेनर में स्टिकी रखने की कोशिश की जाती है, जबकि नॉन-स्टिकी वर्शन में ऐसा नहीं होता है. इसका मतलब है कि स्टिकी वाला पैरालैक्स, बिना:

  • अगर position: sticky कोई एलिमेंट है, तो एलिमेंट के आस-पास z=0 कम एलिमेंट होता है.
  • इसके बिना position: sticky, एलिमेंट z=0 के जितना करीब होगा उतना ही ज़्यादा वह मूव करेगा.

अगर यह सब थोड़ा-बहुत आसान लगता है, तो रॉबर्ट फ़्लैक का यह डेमो देखें. इसमें बताया गया है कि एलिमेंट, स्टिकी पोज़िशन के साथ और उनके बिना, अलग-अलग तरीके से कैसे काम करते हैं. अंतर देखने के लिए, आपको Chrome कैनरी (जो लिखते समय वर्शन 56 है) या Safari की ज़रूरत होगी.

पैरालैक्स के नज़रिए का स्क्रीनशॉट

रॉबर्ट फ़्लैक का डेमो, जिसमें दिखाया गया है कि position: sticky, पैरालैक्स स्क्रोलिंग पर कैसे असर डालता है.

अलग-अलग तरह की गड़बड़ियां और उन्हें हल करने के तरीके

हालांकि, किसी भी चीज़ की तरह, वहां भी उभार और फुंसियां हैं जिन्हें ठीक करने की ज़रूरत है:

  • स्टिकी सहायता अलग-अलग होती है. यह सुविधा अब भी Chrome में लागू की जा रही है, Edge पर यह सुविधा पूरी तरह से काम नहीं करती है. साथ ही, Firefox में गड़बड़ियों को पेंट करने के लिए तब काम किया जाता है, जब स्टिकी को ऐंगल ट्रांसफ़ॉर्म के साथ जोड़ा जाता है. ऐसे मामलों में, ज़रूरत होने पर ही position: sticky (-webkit- प्रीफ़िक्स वाला वर्शन) जोड़ने के लिए थोड़ा कोड जोड़ना फ़ायदेमंद होता है, जो सिर्फ़ Mobile Safari के लिए है.
  • Edge में इफ़ेक्ट "बस काम" नहीं करता. Edge, ओएस के लेवल पर स्क्रोलिंग की कोशिश करता है, जो आम तौर पर अच्छी बात होती है. हालांकि, इस मामले में यह स्क्रोल के दौरान ऐंगल में होने वाले बदलावों का पता नहीं लगा पाता. इसे ठीक करने के लिए, एक तय जगह वाला एलिमेंट जोड़ा जा सकता है. ऐसा इसलिए, क्योंकि इससे Edge को बिना ओएस वाले स्क्रोलिंग तरीके पर स्विच किया जा सकता है. साथ ही, इससे यह पक्का हो जाता है कि इसमें, ऐंगल में होने वाले बदलावों को ध्यान में रखा गया है.
  • "इस पेज का कॉन्टेंट अब बहुत बड़ा हो गया है!" पेज का कॉन्टेंट कितना बड़ा है, यह तय करते समय कई ब्राउज़र स्केल को अहमियत देते हैं, लेकिन अफ़सोस की बात है कि Chrome और Safari किसी खास नज़रिए को ध्यान में नहीं रखते. उदाहरण के लिए, अगर किसी एलिमेंट पर तीन गुना का स्केल लागू किया जाता है, तो आपको स्क्रोल बार और इस तरह की अन्य चीज़ें दिख सकती हैं. भले ही, perspective लागू करने के बाद एलिमेंट की वैल्यू 1x ही क्यों न हो. transform-origin: bottom right के साथ, सबसे नीचे दाएं कोने से एलिमेंट को स्केल करके इस समस्या को हल किया जा सकता है. यह इसलिए काम करता है, क्योंकि इससे एलिमेंट का साइज़ बहुत बड़ा हो जाता है. इससे स्क्रोल किए जाने वाले इलाके के "नेगेटिव क्षेत्र" (आम तौर पर, सबसे ऊपर बाईं ओर) हो जाते हैं. स्क्रोल किए जा सकने वाले रीजन, आपको नेगेटिव क्षेत्र के कॉन्टेंट को न तो देखने देते हैं और न ही उस पर स्क्रोल करते हैं.

नतीजा

पैरालैक्सिंग को सोच-समझकर इस्तेमाल करने पर, यह एक मज़ेदार इफ़ेक्ट है. जैसा कि आपको दिख रहा है, इसे परफ़ॉर्मेंस, स्क्रोल-कपल्ड, और क्रॉस-ब्राउज़र तरीके से लागू किया जा सकता है. इसमें गणित से जुड़ी थोड़ी सी मुश्किलों का सामना करना पड़ता है और मनचाहा इफ़ेक्ट पाने के लिए बॉयलरप्लेट की थोड़ी सी मात्रा का इस्तेमाल किया जाता है. इसलिए, हमने एक छोटी सी हेल्पर लाइब्रेरी और सैंपल तैयार किया है. इसे यूज़र इंटरफ़ेस सैंपल GitHub रेपो में देखा जा सकता है.

खेलें और हमें बताएं कि आपका सफ़र कैसा रहा.