पैरालैक्सिन'

परिचय

हाल ही में पैरलॅक्स साइटों का बहुत ज़्यादा इस्तेमाल किया जा रहा है. इन पर एक नज़र डालें:

अगर आपको इन साइटों के बारे में नहीं पता है, तो हम आपको बता दें कि ये ऐसी साइटें हैं जहां स्क्रोल करने पर पेज का विज़ुअल स्ट्रक्चर बदल जाता है. आम तौर पर, पेज पर मौजूद एलिमेंट, पेज पर स्क्रोल की गई जगह के हिसाब से स्केल होते हैं, घूमते हैं या आगे-पीछे होते हैं.

पैरलॅक्स इफ़ेक्ट वाला डेमो पेज
पैरालैक्स इफ़ेक्ट वाला हमारा डेमो पेज

आपको पैरलॅक्स वाली साइटें पसंद हैं या नहीं, यह एक बात है. हालांकि, यह ज़रूर कहा जा सकता है कि ये साइटें परफ़ॉर्मेंस के लिए काफ़ी खराब हैं. इसकी वजह यह है कि ब्राउज़र को इस तरह ऑप्टिमाइज़ किया जाता है कि जब स्क्रीन को ऊपर या नीचे की ओर स्क्रोल किया जाए, तो नया कॉन्टेंट स्क्रीन पर दिखे. यह इस बात पर निर्भर करता है कि स्क्रीन को किस दिशा में स्क्रोल किया जा रहा है. आम तौर पर, ब्राउज़र तब सबसे अच्छा काम करते हैं, जब स्क्रोल करने के दौरान विज़ुअल में बहुत कम बदलाव होते हैं. पैरलॅक्स वाली साइट के लिए ऐसा शायद ही कभी होता है, क्योंकि कई बार पेज पर मौजूद बड़े विज़ुअल एलिमेंट बदल जाते हैं. इस वजह से, ब्राउज़र को पूरे पेज को फिर से पेंट करना पड़ता है.

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

  • बैकग्राउंड के ऐसे एलिमेंट जो ऊपर और नीचे स्क्रोल करने पर, अपनी पोज़िशन, रोटेशन, और स्केल बदलते हैं.
  • पेज का कॉन्टेंट, जैसे कि टेक्स्ट या छोटी इमेज, जो आम तौर पर ऊपर से नीचे की ओर स्क्रोल होती हैं.

हमने पहले स्क्रोल करने की परफ़ॉर्मेंस और ऐप्लिकेशन के रिस्पॉन्सिवनेस को बेहतर बनाने के तरीकों के बारे में बताया था. यह लेख उसी पर आधारित है. इसलिए, अगर आपने वह लेख नहीं पढ़ा है, तो यह लेख पढ़ना आपके लिए फ़ायदेमंद हो सकता है.

इसलिए, सवाल यह है कि अगर पैरलॅक्स स्क्रोलिंग वाली साइट बनाई जा रही है, तो क्या आपको महंगे रीपेंट करने होंगे या परफ़ॉर्मेंस को बेहतर बनाने के लिए, कोई दूसरा तरीका अपनाया जा सकता है? आइए, हमारे विकल्पों पर एक नज़र डालते हैं.

पहला विकल्प: डीओएम एलिमेंट और एब्सोल्यूट पोज़िशन का इस्तेमाल करना

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

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

Chrome DevTools, जिसमें स्क्रोल इवेंट को डीबाउंस नहीं किया गया है.
DevTools, एक ही फ़्रेम में बड़े पेंट और कई इवेंट-ट्रिगर किए गए लेआउट दिखा रहा है.

ध्यान रखें कि 60fps (60Hz के सामान्य मॉनिटर रीफ़्रेश रेट से मेल खाने वाला) पर काम करने के लिए, हमारे पास सिर्फ़ 16 मिलीसेकंड से ज़्यादा का समय होता है. इस पहले वर्शन में, हर बार स्क्रोल इवेंट मिलने पर हम विज़ुअल अपडेट कर रहे हैं. हालांकि, जैसा कि हमने requestAnimationFrame की मदद से, कम साइज़ वाले बेहतर ऐनिमेशन और स्क्रोल करने की परफ़ॉर्मेंस के बारे में पिछले लेखों में बताया है, यह ब्राउज़र के अपडेट शेड्यूल के साथ मेल नहीं खाता. इसलिए, हमें या तो फ़्रेम नहीं मिलते या हर फ़्रेम में बहुत ज़्यादा काम करना पड़ता है. इससे आपकी साइट पर आसानी से गड़बड़ियां और अस्वाभाविक अनुभव हो सकता है. इससे उपयोगकर्ताओं को निराशा होती है और बिल्ली भी खुश नहीं रहती.

आइए, अपडेट कोड को स्क्रोल इवेंट से requestAnimationFrame कॉलबैक में ले जाएं और स्क्रोल इवेंट के कॉलबैक में स्क्रोल वैल्यू कैप्चर करें.

स्क्रोलिंग टेस्ट को दोहराने पर, आपको थोड़ा सुधार दिख सकता है. हालांकि, यह बहुत ज़्यादा नहीं होगा. इसकी वजह यह है कि स्क्रोल करके ट्रिगर किया जाने वाला लेआउट ऑपरेशन, ज़्यादा महंगा नहीं होता. हालांकि, अन्य इस्तेमाल के उदाहरणों में यह ज़्यादा महंगा हो सकता है. अब हम हर फ़्रेम में कम से कम एक लेआउट ऑपरेशन कर रहे हैं.

Chrome DevTools, जिसमें स्क्रोल इवेंट को डीबाउंस किया गया है.
DevTools, एक ही फ़्रेम में बड़े पेंट और कई इवेंट-ट्रिगर किए गए लेआउट दिखा रहा है.

अब हम हर फ़्रेम में एक या सौ स्क्रोल इवेंट मैनेज कर सकते हैं. हालांकि, जब भी requestAnimationFrame कॉलबैक चलता है और विज़ुअल अपडेट करता है, तब हम सिर्फ़ सबसे हाल की वैल्यू का इस्तेमाल करते हैं. इसका मतलब है कि अब आपको हर बार स्क्रोल इवेंट मिलने पर, विज़ुअल अपडेट करने की ज़रूरत नहीं है. अब आपको ब्राउज़र से, अपडेट करने के लिए सही विंडो देने का अनुरोध करना है. क्या आप बहुत अच्छे नहीं हैं?

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

दूसरा विकल्प: DOM एलिमेंट और 3D ट्रांसफ़ॉर्म का इस्तेमाल करना

एलिमेंट पर 3D ट्रांसफ़ॉर्म लागू करके, ऐब्सलूट पोज़िशन का इस्तेमाल करने के बजाय, कोई दूसरा तरीका भी अपनाया जा सकता है. इस स्थिति में, हमें पता चलता है कि 3D ट्रांसफ़ॉर्म लागू किए गए एलिमेंट को हर एलिमेंट के लिए एक नई लेयर दी जाती है. साथ ही, WebKit ब्राउज़र में, अक्सर हार्डवेयर कंपोजिटर पर स्विच भी होता है. इसके उलट, पहले विकल्प में हमारे पास पेज के लिए एक बड़ी लेयर थी, जिसे किसी भी बदलाव के होने पर फिर से पेंट करना पड़ता था. साथ ही, पेज को पेंट करने और कॉम्पोज़ करने की पूरी प्रोसेस को सीपीयू मैनेज करता था.

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

कई बार लोग सिर्फ़ -webkit-transform: translateZ(0); हैक का इस्तेमाल करके, परफ़ॉर्मेंस में काफ़ी सुधार देखते हैं. हालांकि, यह तरीका आज भी काम करता है, लेकिन इसमें ये समस्याएं आती हैं:

  1. यह अलग-अलग ब्राउज़र के साथ काम नहीं करता.
  2. यह हर बदले गए एलिमेंट के लिए एक नई लेयर बनाकर, ब्राउज़र को मजबूर करता है. बहुत सारी लेयर से परफ़ॉर्मेंस पर असर पड़ सकता है. इसलिए, इनका इस्तेमाल कम से कम करें!
  3. इसे कुछ वेबवर्क पोर्ट के लिए बंद कर दिया गया है (सबसे नीचे चौथा बुलेट!).

अगर आपको 3D में अनुवाद की सुविधा का इस्तेमाल करना है, तो सावधान रहें. यह आपकी समस्या का कुछ समय के लिए ही समाधान है! आम तौर पर, हमें 2D ट्रांसफ़ॉर्म से रेंडरिंग की ऐसी ही विशेषताएं दिखेंगी जैसी कि 3D से दिखती हैं. ब्राउज़र की परफ़ॉर्मेंस लगातार बेहतर हो रही है. इसलिए, हमें उम्मीद है कि इससे पहले ही हम इस सुविधा का इस्तेमाल कर पाएंगे.

आखिर में, आपको जहां भी हो सके वहां पेंट से बचना चाहिए और पेज पर मौजूद एलिमेंट को इधर-उधर ले जाना चाहिए. उदाहरण के लिए, पैरलॅक्स वाली साइटों में आम तौर पर, तय ऊंचाई वाले डिव का इस्तेमाल किया जाता है. साथ ही, इफ़ेक्ट देने के लिए, उनके बैकग्राउंड की पोज़िशन बदली जाती है. माफ़ करें, इसका मतलब है कि हर पास पर एलिमेंट को फिर से पेंट करना होगा. इससे परफ़ॉर्मेंस पर असर पड़ सकता है. इसके बजाय, अगर हो सके, तो एलिमेंट बनाएं (ज़रूरत पड़ने पर, इसे overflow: hidden के साथ किसी div में रैप करें) और इसके बजाय, इसका अनुवाद करें.

तीसरा विकल्प: फ़िक्स की गई पोज़िशन वाले कैनवस या WebGL का इस्तेमाल करना

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

  • अब हमें कंपोज़िटर के ज़्यादा काम की ज़रूरत नहीं है, क्योंकि इसमें सिर्फ़ एक एलिमेंट, कैनवस है.
  • हम एक हार्डवेयर से तेज़ किए गए बिटमैप के साथ काम कर रहे हैं.
  • Canvas2D API, हमारे ट्रांसफ़ॉर्मेशन के लिए एक बेहतरीन विकल्प है. इसका मतलब है कि डेवलपमेंट और रखरखाव को आसानी से मैनेज किया जा सकता है.

कैनवस एलिमेंट का इस्तेमाल करने पर, हमें एक नई लेयर मिलती है, लेकिन यह सिर्फ़ एक लेयर होती है. वहीं, दूसरे विकल्प में हमें 3D ट्रांसफ़ॉर्म लागू किए गए हर एलिमेंट के लिए एक नई लेयर मिलती है. इसलिए, उन सभी लेयर को एक साथ कॉम्पोज़ करने में ज़्यादा काम करना पड़ता है. ट्रांसफ़ॉर्मेशन को अलग-अलग ब्राउज़र पर लागू करने के तरीकों में अंतर होने की वजह से, यह आज सबसे बेहतर समाधान है.


/**
 * Updates and draws in the underlying visual elements to the canvas.
 */
function updateElements () {

  var relativeY = lastScrollY / h;

  // Fill the canvas up
  context.fillStyle = "#1e2124";
  context.fillRect(0, 0, canvas.width, canvas.height);

  // Draw the background
  context.drawImage(bg, 0, pos(0, -3600, relativeY, 0));

  // Draw each of the blobs in turn
  context.drawImage(blob1, 484, pos(254, -4400, relativeY, 0));
  context.drawImage(blob2, 84, pos(954, -5400, relativeY, 0));
  context.drawImage(blob3, 584, pos(1054, -3900, relativeY, 0));
  context.drawImage(blob4, 44, pos(1400, -6900, relativeY, 0));
  context.drawImage(blob5, -40, pos(1730, -5900, relativeY, 0));
  context.drawImage(blob6, 325, pos(2860, -7900, relativeY, 0));
  context.drawImage(blob7, 725, pos(2550, -4900, relativeY, 0));
  context.drawImage(blob8, 570, pos(2300, -3700, relativeY, 0));
  context.drawImage(blob9, 640, pos(3700, -9000, relativeY, 0));

  // Allow another rAF call to be scheduled
  ticking = false;
}

/**
 * Calculates a relative disposition given the page's scroll
 * range normalized from 0 to 1
 * @param {number} base The starting value.
 * @param {number} range The amount of pixels it can move.
 * @param {number} relY The normalized scroll value.
 * @param {number} offset A base normalized value from which to start the scroll behavior.
 * @returns {number} The updated position value.
 */
function pos(base, range, relY, offset) {
  return base + limit(0, 1, relY - offset) * range;
}

/**
 * Clamps a number to a range.
 * @param {number} min The minimum value.
 * @param {number} max The maximum value.
 * @param {number} value The value to limit.
 * @returns {number} The clamped value.
 */
function limit(min, max, value) {
  return Math.max(min, Math.min(max, value));
}

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

हम इस सुविधा को बेहतर बनाने की पूरी कोशिश कर रहे हैं. इसलिए, यह मानने की कोई वजह नहीं है कि पैरलॅक्स इफ़ेक्ट का इस्तेमाल, कैनवस एलिमेंट में ही किया जाना चाहिए. अगर ब्राउज़र पर WebGL काम करता है, तो हम इसका इस्तेमाल कर सकते हैं. यहां अहम बात यह है कि WebGL में, ग्राफ़िक्स कार्ड के लिए सभी एपीआई का सबसे सीधा रास्ता है. इसलिए, 60fps हासिल करने के लिए, यह सबसे सही विकल्प है. खास तौर पर, अगर साइट के इफ़ेक्ट जटिल हैं.

ऐसा हो सकता है कि आपका पहला रिस्पॉन्स यह हो कि WebGL का इस्तेमाल ज़रूरत से ज़्यादा है या यह हर जगह काम नहीं करता. हालांकि, अगर Three.js जैसा कोई टूल इस्तेमाल किया जाता है, तो कैनवस एलिमेंट का इस्तेमाल किया जा सकता है. इससे आपका कोड आसानी से समझा जा सकता है और उसे लगातार इस्तेमाल किया जा सकता है. एपीआई के साथ काम करने वाले ब्राउज़र की जांच करने के लिए, हमें बस Modernizr का इस्तेमाल करना होगा:

// check for WebGL support, otherwise switch to canvas
if (Modernizr.webgl) {
  renderer = new THREE.WebGLRenderer();
} else if (Modernizr.canvas) {
  renderer = new THREE.CanvasRenderer();
}

इस तरीके के बारे में आखिरी बात यह है कि अगर आपको पेज में अतिरिक्त एलिमेंट जोड़ने में दिलचस्पी नहीं है, तो Firefox और WebKit पर आधारित ब्राउज़र, दोनों में बैकग्राउंड एलिमेंट के तौर पर कैनवस का इस्तेमाल किया जा सकता है. हालांकि, यह सुविधा हर जगह उपलब्ध नहीं है. इसलिए, हमेशा की तरह आपको इस सुविधा का इस्तेमाल सावधानी से करना चाहिए.

यह आपको तय करना है

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

ट्रांसफ़ॉर्म, खास तौर पर 3D ट्रांसफ़ॉर्म की मदद से, सीधे तौर पर DOM एलिमेंट के साथ काम किया जा सकता है और बेहतर फ़्रेम रेट हासिल किया जा सकता है. यहां सफल होने के लिए, जहां भी हो सके वहां पेंट करने से बचें और एलिमेंट को एक से दूसरी जगह ले जाने की कोशिश करें. ध्यान रखें कि WebKit ब्राउज़र लेयर बनाने का तरीका, ज़रूरी नहीं है कि दूसरे ब्राउज़र इंजन से मेल खाए. इसलिए, इस तरीके को अपनाने से पहले, इसकी जांच ज़रूर करें.

अगर आपको सिर्फ़ टॉप टीयर के ब्राउज़र के लिए साइट बनानी है और आपके पास कैनवस का इस्तेमाल करके साइट को रेंडर करने की सुविधा है, तो यह आपके लिए सबसे अच्छा विकल्प हो सकता है. अगर आपको Three.js का इस्तेमाल करना है, तो आपको अपनी ज़रूरत के हिसाब से रेंडरर को आसानी से स्विच और बदलने में मदद मिलेगी.

नतीजा

हमने पैरलॅक्स साइटों से जुड़ी समस्याओं को हल करने के लिए, कुछ तरीकों का आकलन किया है. इनमें, एलिमेंट को पूरी तरह से तय जगह पर रखना और तय जगह पर कैनवस का इस्तेमाल करना शामिल है. यह ज़रूरी नहीं है कि आपने जो रणनीति चुनी है वह आपके लिए सबसे सही हो. यह इस बात पर निर्भर करता है कि आपको क्या हासिल करना है और आपके पास कौनसे विकल्प हैं.

हमेशा की तरह, कोई भी तरीका आज़माने से पहले: अंदाज़ा न लगाएं, टेस्ट करें.