ब्लर को ऐनिमेट करना

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

कम शब्दों में कहा जाए तो

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

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

समस्या

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

फ़िलहाल, हम धुंधला करने की सुविधा को ऐनिमेट नहीं कर सकते. हालांकि, हम ऐसा तरीका ढूंढ सकते हैं जो काफ़ी अच्छा लगे, लेकिन तकनीकी तौर पर, ऐनिमेशन वाला धुंधलापन न हो. शुरू करने के लिए, आइए पहले यह समझते हैं कि ऐनिमेशन वाला धुंधलापन क्यों धीमा होता है. वेब पर एलिमेंट को धुंधला करने के लिए, दो तकनीकें इस्तेमाल की जा सकती हैं: सीएसएस filter प्रॉपर्टी और SVG फ़िल्टर. आम तौर पर, सीएसएस फ़िल्टर का इस्तेमाल किया जाता है, क्योंकि इनका इस्तेमाल करना आसान है और इनके लिए ज़्यादा सहायता मिलती है. माफ़ करें, अगर आपको Internet Explorer के साथ काम करना है, तो आपके पास SVG फ़िल्टर इस्तेमाल करने के अलावा कोई विकल्प नहीं है. ऐसा इसलिए है, क्योंकि IE 10 और 11 में SVG फ़िल्टर काम करते हैं, लेकिन CSS फ़िल्टर नहीं. अच्छी बात यह है कि धुंधला करने की दोनों तकनीकों के साथ, ऐनिमेशन देने का हमारा तरीका काम करता है. इसलिए, DevTools की मदद से समस्या का पता लगाने की कोशिश करते हैं.

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

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

यहीं समस्या है. हम हर फ़्रेम के लिए, जीपीयू पर ज़्यादा खर्चीली प्रोसेस कर रहे हैं. इससे, हमारे फ़्रेम के बजट को 16 मि॰से॰ से ज़्यादा समय लग रहा है. इसलिए, फ़्रेम रेट 60 फ़्रेम प्रति सेकंड से काफ़ी कम हो रहा है.

रैबिट होल

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

क्रॉस-फ़ेड, ओवरलैप होने वाले ओपैसिटी फ़ेड-इन और फ़ेड-आउट की एक सीरीज़ होती है. उदाहरण के लिए, अगर हमारे पास धुंधला करने के चार चरण हैं, तो हम पहले चरण को धीरे-धीरे हटाते हुए, दूसरे चरण को धीरे-धीरे लाते हैं. जब दूसरा चरण 100% ऑपैसिटी पर पहुंच जाता है और पहला चरण 0% पर पहुंच जाता है, तो हम तीसरे चरण को फ़ेड इन करते हुए, दूसरे चरण को फ़ेड आउट कर देते हैं. इसके बाद, हम तीसरे चरण को धीरे-धीरे हटा देते हैं और चौथे और आखिरी वर्शन को धीरे-धीरे दिखाते हैं. इस स्थिति में, हर चरण में कुल समय का ¼ हिस्सा लगेगा. यह विज़ुअल तौर पर, रीयल और ऐनिमेशन वाले ब्लर से काफ़ी मिलता-जुलता है.

हमारे प्रयोगों में, हर चरण में धुंधला करने की त्रिज्या को बढ़ाने से, सबसे बेहतर विज़ुअल नतीजे मिले. उदाहरण: अगर हमारे पास धुंधला करने के चार चरण हैं, तो हम हर चरण में filter: blur(2^n) लागू करेंगे. जैसे, चरण 0: 1 पिक्सल, चरण 1: 2 पिक्सल, चरण 2: 4 पिक्सल, और चरण 3: 8 पिक्सल. अगर हम will-change: transform का इस्तेमाल करके, धुंधली की गई इन सभी कॉपी को अपनी लेयर पर फ़ोर्स करते हैं, तो इन एलिमेंट की ओपैसिटी को बहुत तेज़ी से बदला जा सकता है. इसे "प्रमोशन" कहा जाता है. सिद्धांत रूप से, इससे हमें फ़ोटो को धुंधला करने के महंगे काम को पहले से लोड करने में मदद मिलेगी. ऐसा लगता है कि लॉजिक में कोई गड़बड़ी है. इस डेमो को चलाने पर, आपको दिखेगा कि फ़्रेम रेट अब भी 60fps से कम है. साथ ही, वीडियो पहले से ज़्यादा धुंधला दिख रहा है.

DevTools में, ट्रैक का एक उदाहरण, जिसमें GPU का इस्तेमाल ज़्यादा समय तक किया गया है.

DevTools में एक नज़र डालने से पता चलता है कि GPU अब भी बहुत व्यस्त है और हर फ़्रेम को ~90 मिलीसेकंड तक खींचता है. लेकिन क्यों? हम अब धुंधला करने की वैल्यू नहीं बदल रहे हैं, सिर्फ़ ओपैसिटी बदल रहे हैं. तो क्या हो रहा है? समस्या, फिर से धुंधला करने वाले इफ़ेक्ट के तौर पर है: जैसा कि पहले बताया गया है, अगर एलिमेंट को प्रमोट और धुंधला किया जाता है, तो इफ़ेक्ट को GPU लागू करता है. इसलिए, भले ही हम अब ब्लर वैल्यू को ऐनिमेट नहीं कर रहे हैं, लेकिन टेक्सचर अब भी ब्लर नहीं हुआ है और जीपीयू को हर फ़्रेम में उसे फिर से ब्लर करना होगा. फ़्रेम रेट के पहले से भी खराब होने की वजह यह है कि सामान्य तरीके से लागू करने की तुलना में, GPU को असल में पहले से ज़्यादा काम करना पड़ता है. ऐसा इसलिए होता है, क्योंकि ज़्यादातर समय दो टेक्सचर दिखते हैं जिन्हें अलग-अलग धुंधला करना पड़ता है.

हमने जो तरीका अपनाया है वह बहुत अच्छा नहीं है, लेकिन इससे ऐनिमेशन बहुत तेज़ी से चलता है. हम धुंधला किए जाने वाले एलिमेंट का प्रमोशन नहीं करते, बल्कि इसके बजाय पैरंट रैपर का प्रमोशन करते हैं. अगर किसी एलिमेंट को धुंधला और प्रमोट किया जाता है, तो इफ़ेक्ट को जीपीयू लागू करता है. इस वजह से, हमारा डेमो धीमा हो गया. अगर एलिमेंट को धुंधला किया गया है, लेकिन उसे प्रमोट नहीं किया गया है, तो धुंधलापन को पास के पैरंट टेक्सचर में रेस्टर किया जाता है. हमारे मामले में, यह प्रमोशन किया गया पैरंट रैपर एलिमेंट है. धुंधली की गई इमेज अब पैरंट एलिमेंट का टेक्स्चर बन जाती है. आने वाले समय में सभी फ़्रेम के लिए इसका फिर से इस्तेमाल किया जा सकता है. यह सिर्फ़ इसलिए काम करता है, क्योंकि हमें पता है कि धुंधले किए गए एलिमेंट ऐनिमेशन नहीं करते और उन्हें कैश मेमोरी में सेव करने से फ़ायदा होता है. यहां एक ऐसा डेमो दिया गया है जिसमें इस तकनीक को लागू किया गया है. मुझे आश्चर्य है कि Moto G4 इस तरीके के बारे में क्या सोचता है? स्पॉइलर अलर्ट: इसे लगता है कि यह बहुत बढ़िया है:

DevTools में ट्रैक दिखाया गया है, जिसमें GPU का बहुत ज़्यादा समय खाली है.

अब हमारे पास जीपीयू के लिए ज़्यादा जगह है और 60fps पर बेहतरीन परफ़ॉर्मेंस मिल रही है. हमने कर दिखाया!

प्रोडक्शन में भेजना

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

ज़्यादातर लोग शैडो डीओएम को अपने कस्टम एलिमेंट में "इंटरनल" एलिमेंट अटैच करने के तरीके के तौर पर देखते हैं. हालांकि, यह अलगाव और परफ़ॉर्मेंस के लिए भी एक प्राइमिटिव है! JavaScript और CSS, शैडो DOM की सीमाओं को पार नहीं कर सकते. इसकी मदद से, डेवलपर की स्टाइल या ऐप्लिकेशन लॉजिक में बदलाव किए बिना कॉन्टेंट को डुप्लीकेट किया जा सकता है. हमारे पास पहले से ही, रैस्टर करने के लिए हर कॉपी के लिए एक <div> एलिमेंट है. अब हम इन <div> एलिमेंट का इस्तेमाल शैडो होस्ट के तौर पर करते हैं. हम attachShadow({mode: 'closed'}) का इस्तेमाल करके ShadowRoot बनाते हैं और कॉन्टेंट की कॉपी को <div> के बजाय ShadowRoot से अटैच करते हैं. हमें यह पक्का करना होगा कि सभी स्टाइलशीट को ShadowRoot में भी कॉपी किया जाए, ताकि यह पक्का किया जा सके कि हमारी कॉपी की स्टाइल, ओरिजनल की तरह ही हो.

कुछ ब्राउज़र, शैडो DOM v1 के साथ काम नहीं करते. ऐसे में, हम कॉन्टेंट को डुप्लीकेट करते हैं और उम्मीद करते हैं कि कोई समस्या न आए. हम ShadyCSS के साथ शैडो DOM पॉलीफ़िल का इस्तेमाल कर सकते थे, लेकिन हमने अपनी लाइब्रेरी में इसे लागू नहीं किया.

बस, यही है. Chrome की रेंडरिंग पाइपलाइन के बारे में जानने के बाद, हमने यह पता लगा लिया है कि हम सभी ब्राउज़र पर धुंधलापन को बेहतर तरीके से ऐनिमेट कैसे कर सकते हैं!

नतीजा

इस तरह के इफ़ेक्ट का इस्तेमाल हल्के में नहीं किया जाना चाहिए. हम DOM एलिमेंट को कॉपी करके, उन्हें अपनी लेयर पर फ़ोर्स करते हैं. इस वजह से, हम लो-एंड डिवाइसों की परफ़ॉर्मेंस को बेहतर बना सकते हैं. हर ShadowRoot में सभी स्टाइलशीट कॉपी करने से, परफ़ॉर्मेंस पर असर पड़ सकता है. इसलिए, आपको यह तय करना होगा कि आपको LightDOM में कॉपी का असर न पड़े, इसके लिए अपने लॉजिक और स्टाइल में बदलाव करना है या ShadowDOM की तकनीक का इस्तेमाल करना है. हालांकि, कभी-कभी हमारी तकनीक का इस्तेमाल करना फ़ायदेमंद हो सकता है. हमारे GitHub डेटा स्टोर में मौजूद कोड और डेमो देखें. अगर आपका कोई सवाल है, तो Twitter पर हमसे संपर्क करें!