बेहतर रेंडरिंग के लिए जैंक बस्टिंग

Tom Wiltzius
Tom Wiltzius

परिचय

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

यह लेखों की सीरीज़ में पहला है, जिसमें ब्राउज़र में रेंडरिंग की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने की जानकारी दी गई है. शुरुआत में हम आपको बताएंगे कि आसान ऐनिमेशन क्यों मुश्किल है और इसे हासिल करने के लिए क्या करना होगा. साथ ही, हम कुछ आसान सबसे सही तरीकों के बारे में भी बात करेंगे. इनमें से कई विचार मूल रूप से "जैंक बस्टर" एक बातचीत जो Nat Duca और मैंने इस साल Google I/O बातचीत (वीडियो) में दी थी.

पेश है वी-सिंक

कंप्यूटर गेमर को इस शब्द के बारे में पता होगा, लेकिन वेब पर यह आम बात नहीं है: v-sync क्या होता है?

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

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

सही समय का पता लगाना बेहद ज़रूरी है: requestAnimationFrame

कई वेब डेवलपर ऐनिमेशन बनाने के लिए हर 16 मिलीसेकंड में setInterval या setTimeout का इस्तेमाल करते हैं. यह समस्या कई वजहों से है (और हम इस पर एक मिनट में चर्चा करेंगे), लेकिन खास तौर पर ये समस्याएं हैं:

  • JavaScript से टाइमर रिज़ॉल्यूशन सिर्फ़ कई मिलीसेकंड के क्रम में होता है
  • अलग-अलग डिवाइसों की रीफ़्रेश दरें अलग-अलग होती हैं

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

अलग-अलग डिसप्ले में रीफ़्रेश दर अलग-अलग होती है: आम तौर पर, कुछ फ़ोन में रीफ़्रेश दर 60 हर्ट्ज़, कुछ फ़ोन 59 हर्ट्ज़ और कुछ लैपटॉप के लो पावर मोड में 50 हर्ट्ज़ की दर से कम हो जाती है. वहीं, कुछ डेस्कटॉप मॉनिटर का तापमान 70 हर्ट्ज़ पर सेट हो जाता है.

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

सही समय पर ऐनिमेशन फ़्रेम पाने का तरीका requestAnimationFrame है. इस एपीआई का इस्तेमाल करते समय, ब्राउज़र से एक ऐनिमेशन फ़्रेम मांगा जाता है. जब ब्राउज़र जल्द ही नया फ़्रेम बनाने वाला होता है, तो आपके कॉलबैक को कॉल किया जाता है. रीफ़्रेश दर चाहे जो भी हो, ऐसा किया जाता है.

requestAnimationFrame में अन्य अच्छी प्रॉपर्टी भी हैं:

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

requestAnimationFrame की चर्चा सभी जगह पहले ही हो चुकी है, इसलिए इसके बारे में ज़्यादा जानकारी के लिए क्रिएटिव JS के इस लेख जैसे लेख देखें. हालांकि, यह ऐनिमेशन को आसान बनाने के लिए पहला ज़रूरी कदम है.

बजट फ़्रेम करें

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

16 मि॰से॰ ज़्यादा समय नहीं होता है. अच्छी बात यह है कि अगर आप requestAnimationFrame कॉलबैक के दौरान अपना फ़्रेम बजट ब्लो कर रहे हों, तो Chrome के डेवलपर टूल यह ट्रैक करने में आपकी मदद कर सकते हैं.

Dev टूल की टाइमलाइन खोलना और इस ऐनिमेशन की रिकॉर्डिंग करते हुए यह तुरंत पता चलता है कि ऐनिमेट करते समय हमने काफ़ी बजट पार कर लिया है. टाइमलाइन में "फ़्रेम" पर स्विच करें और इस पर एक नज़र डालें:

बहुत ज़्यादा लेआउट वाला डेमो
बहुत ज़्यादा लेआउट वाला डेमो

इन requestAnimationFrame (rAF) कॉलबैक को 200 मि॰से॰ से ज़्यादा समय लगता है. यह मात्रा इतनी ज़्यादा है कि हर 16 मि॰से॰ में एक फ़्रेम के लिए समय का पता लगाया जा सकता है! इन लंबे rAF कॉलबैक में से एक को खोलने से पता चलता है कि अंदर क्या चल रहा है: इस मामले में, बहुत सारे लेआउट.

पॉल के वीडियो में, रिलेआउट की खास वजह (यह scrollTop पढ़ा गया है) और इससे बचने के तरीके के बारे में ज़्यादा जानकारी दी गई है. हालांकि, यहां बात यह है कि आप कॉलबैक में जाकर यह जांच कर सकते हैं कि क्या ज़्यादा समय लग रहा है.

बहुत कम लेआउट वाला अपडेट किया गया डेमो
बहुत कम लेआउट वाला अपडेट किया गया डेमो

16 मि॰से॰ फ़्रेम टाइम पर ध्यान दें. फ़्रेम में वह खाली जगह वह हेडरूम है जिसे आपको ज़्यादा काम करना है (या ब्राउज़र को वह काम करने दें जो उसे बैकग्राउंड में करने देना है). उस खाली जगह से ही कोई फ़ायदा होता है.

जैंक के अन्य स्रोत

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

ऐसी स्थितियों से बचने का कोई जादुई तरीका नहीं है. हालांकि, सफलता हासिल करने के लिए, आर्किटेक्चर से जुड़े कुछ सबसे सही तरीके यहां दिए गए हैं:

  • इनपुट हैंडलर में बहुत ज़्यादा प्रोसेसिंग न करें! बहुत सारा JS करना या पूरे पेज को फिर से व्यवस्थित करने की कोशिश करना, उदाहरण के लिए ऑनस्क्रोल हैंडलर खराब समस्याओं की एक आम वजह है.
  • अपने rAF कॉलबैक या वेब वर्कर में ज़्यादा से ज़्यादा प्रोसेसिंग (पढ़ें: ऐसी कोई भी चीज़ जिसे चलाने में ज़्यादा समय लगेगा) पुश करें.
  • अगर आप काम को rAF कॉलबैक में करते हैं, तो इसे अलग-अलग करने की कोशिश करें, ताकि आप हर फ़्रेम के लिए थोड़ा-बहुत प्रोसेस कर सकें या ज़रूरी ऐनिमेशन खत्म होने तक इसे टाल दें - इस तरह आप छोटे rAF कॉलबैक को चलाना और आसानी से ऐनिमेट करना जारी रख सकेंगे.

प्रोसेसिंग को इनपुट हैंडलर के बजाय requestAnimationFrame कॉलबैक में कैसे पुश किया जाता है, इस बारे में जानकारी देने वाला शानदार ट्यूटोरियल देखने के लिए, पॉल लुइस का यह लेख पढ़ें: Leaner, Meaner, Fast Animations with requestAnimationFrame.

सीएसएस ऐनिमेशन

आपके इवेंट और rAF कॉलबैक में लाइटवेट जेएस से बेहतर क्या हो सकता है? कोई JS नहीं.

पहले हमने कहा था कि आपके rAF कॉलबैक से बचने के लिए कोई आसान उपाय नहीं है, लेकिन इनकी पूरी तरह से ज़रूरत से बचने के लिए CSS ऐनिमेशन का इस्तेमाल किया जा सकता है. खास तौर से Android के लिए Chrome पर (और दूसरे ब्राउज़र मिलती-जुलती सुविधाओं पर काम कर रहे हैं), सीएसएस ऐनिमेशन में काफ़ी पसंद की प्रॉपर्टी होती है. JavaScript के चलने पर भी ब्राउज़र, अक्सर इन्हें चला सकता है.

जैंक के बारे में ऊपर दिए गए सेक्शन में एक इंप्लिसिट स्टेटमेंट है: ब्राउज़र एक समय में सिर्फ़ एक काम कर सकते हैं. यह पूरी तरह से सही नहीं है, लेकिन एक बेहतर धारणा है कि: किसी खास समय पर ब्राउज़र JS, लेआउट या पेंटिंग चला रहा हो सकता है, लेकिन एक बार में सिर्फ़ एक ही काम कर सकता है. इसकी पुष्टि, Dev टूल के टाइमलाइन व्यू में की जा सकती है. Android के लिए Chrome पर सीएसएस ऐनिमेशन, इस नियम का एक अपवाद है. जल्द ही, यह Chrome के डेस्कटॉप वर्शन पर भी लागू होगा. हालांकि, अभी ऐसा नहीं होगा.

जब संभव हो, सीएसएस ऐनिमेशन का इस्तेमाल करने से आपका ऐप्लिकेशन आसान हो जाता है और JavaScript के चलने के दौरान भी ऐनिमेशन आसानी से चलता रहता है.

  // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ for info on rAF polyfills
  rAF = window.requestAnimationFrame;

  var degrees = 0;
  function update(timestamp) {
    document.querySelector('#foo').style.webkitTransform = "rotate(" + degrees + "deg)";
    console.log('updated to degrees ' + degrees);
    degrees = degrees + 1;
    rAF(update);
  }
  rAF(update);

अगर आप JavaScript के 180 मि॰से॰ तक चलने वाले बटन पर क्लिक करते हैं, तो जैंक होने लगता है. लेकिन अगर इसके बजाय हम उस एनिमेशन को CSS एनिमेशन के साथ चलाते हैं, तो जैंक अब नहीं होता है.

(इस लेख को लिखते समय, याद रखें कि सीएसएस ऐनिमेशन, Android के लिए Chrome पर सिर्फ़ जैंक-फ़्री होता है, डेस्कटॉप Chrome पर नहीं.)

  /* tools like Modernizr (http://modernizr.com/) can help with CSS polyfills */
  #foo {
    +animation-duration: 3s;
    +animation-timing-function: linear;
    +animation-animation-iteration-count: infinite;
    +animation-animation-name: rotate;
  }

  @+keyframes: rotate; {
    from {
      +transform: rotate(0deg);
    }
    to {
      +transform: rotate(360deg);
    }
  }

सीएसएस ऐनिमेशन का इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए, एमडीएन पर इसके बारे में इस तरह के लेख देखें.

रैप अप

इसका शॉर्ट वीडियो यह है:

  1. ऐनिमेट करते समय, हर स्क्रीन रीफ़्रेश के लिए फ़्रेम बनाना बहुत ज़रूरी होता है. Vsync'd ऐनिमेशन, ऐप्लिकेशन के इस्तेमाल के अनुभव पर गहरा असर डालता है.
  2. Chrome और अन्य आधुनिक ब्राउज़र में vsync' ऐनिमेशन पाने का सबसे अच्छा तरीका यह है CSS एनिमेशन का उपयोग करने के लिए. जब आपको सीएसएस ऐनिमेशन से ज़्यादा सुविधाओं की ज़रूरत हो के कॉन्टेंट में इस्तेमाल किया जाता है, तो सबसे अच्छी तकनीक है requestAnimationFrame पर आधारित ऐनिमेशन.
  3. rAF ऐनिमेशन को अच्छी तरह से चलाने के लिए, पक्का करें कि दूसरे इवेंट हैंडलर आपके rAF कॉलबैक को चलाने में समस्या नहीं आ रही है और rAF कॉलबैक रखें कम (15 मि॰से॰ से कम).

आखिर में, vsync'd ऐनिमेशन सिर्फ़ सामान्य यूज़र इंटरफ़ेस (यूआई) ऐनिमेशन पर ही लागू नहीं होता, बल्कि यह Canvas2D ऐनिमेशन, WebGL ऐनिमेशन, और यहां तक कि स्टैटिक पेजों पर स्क्रोल करने पर भी लागू होता है. इस सीरीज़ के अगले लेख में, हम इन कॉन्सेप्ट को ध्यान में रखते हुए, स्क्रोल करने की परफ़ॉर्मेंस के बारे में ज़्यादा जानेंगे.

ऐनिमेशन के लिए शुभकामनाएं!

रेफ़रंस