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

शुरुआती जानकारी

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

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

डेमो पैरालैक्स पेज
पैरालैक्स इफ़ेक्ट के साथ हमारा डेमो पेज पूरा हुआ

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

इस तरह की पैरलैक्सिंग साइट को सामान्य बनाना सही है:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

आखिर में, आपको कोशिश करनी चाहिए कि जहां मुमकिन हो, वहां पेंट न हो. साथ ही, मौजूदा एलिमेंट को पेज पर एक जगह से दूसरी जगह ले जाना हो. उदाहरण के लिए, पैरालैक्स साइटों में यह एक सामान्य तरीका है, जिसमें तय ऊंचाई वाले div का इस्तेमाल किया जाता है. साथ ही, इफ़ेक्ट देने के लिए बैकग्राउंड की स्थिति बदली जाती है. इसका मतलब यह है कि हर पास पर एलिमेंट को फिर से पेंट करने की ज़रूरत है. यह परफ़ॉर्मेंस के मामले में आपके लिए काफ़ी फ़ायदेमंद साबित हो सकता है. इसके बजाय, अगर हो सके, तो आपको एलिमेंट बनाना (ज़रूरी होने पर इसे 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 फ़ॉर्मैट में उपलब्ध है. इसकी मदद से, सीधे डीओएम एलिमेंट के साथ काम किया जा सकता है और एक अच्छा फ़्रेम रेट हासिल किया जा सकता है. यहां सफलता पाने की कुंजी यह है कि जहां भी हो सके वहां पेंटिंग न करें और बस एलिमेंट को इधर-उधर करने की कोशिश करें. ध्यान रखें कि वेबकिट ब्राउज़र जिस तरह से लेयर बनाते हैं, ज़रूरी नहीं है उसका संबंध अन्य ब्राउज़र इंजन से भी नहीं हो सकता. इसलिए, इस समाधान को आज़माने से पहले इसकी जांच ज़रूर कर लें.

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

नतीजा

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

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