कंपोज़ में जेलिफ़िश को स्थानांतरित करना: छवि वेक्टर को एनिमेट करना और AGSL रेंडर प्रभाव लागू करना
मुझे इंटरनेट पर प्रेरक लोगों का अनुसरण करना और यह देखना पसंद है कि वे क्या बनाते हैं - ऐसी ही एक व्यक्ति हैं कैसी कोड्स , वह वेब के लिए अविश्वसनीय एनिमेशन बनाती हैं। उसके प्रेरक उदाहरणों में से एक यह प्यारा एनिमेटेड जेलिफ़िश है ।
इसे देखने के बाद और कुछ देर तक इस पर ध्यान देने के बाद, मैं मन ही मन सोचता रहा कि इस प्यारे से छोटे जीव को कंपोज़ में भी ज़िंदा होने की ज़रूरत है। तो इस ब्लॉग पोस्ट में बताया गया है कि मैंने जेटपैक कंपोज़ में इसे कैसे बनाया, अंतिम कोड यहां पाया जा सकता है । यहां की तकनीकें न केवल जेलिफ़िश के लिए प्रासंगिक हैं ... कोई अन्य मछली भी करेगी! मज़ाक कर रहा हूँ - यह ब्लॉग पोस्ट कवर करेगा:
- कस्टम छवि वेक्टर
- एनिमेटिंग इमेजवेक्टर पथ या समूह
- AGSL RenderEffect के साथ कम्पोज़ेबल पर डिस्टॉर्शन नॉइज़ इफ़ेक्ट लागू करना।
एसवीजी का विश्लेषण
इस जेलिफ़िश को लागू करने के लिए, हमें यह देखने की ज़रूरत है कि एसवीजी पहले क्या बना है - और इसके विभिन्न भागों को दोहराने का प्रयास करें। एसवीजी क्या चित्रित कर रहा है, यह पता लगाने का सबसे अच्छा तरीका है कि इसके विभिन्न हिस्सों पर टिप्पणी करें और एसवीजी के प्रत्येक खंड के दृश्य परिणाम देखें। ऐसा करने के लिए, आप या तो इसे ऊपर लिंक किए गए कोडपेन में बदल सकते हैं, या टेक्स्ट एडिटर में एक एसवीजी डाउनलोड और खोल सकते हैं (यह एक टेक्स्ट पठनीय प्रारूप है)।
तो आइए इस एसवीजी पर एक नजर डालते हैं:
<!--
Jellyfish SVG, path data removed for brevity
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 530.46 563.1">
<defs>
<filter id="turbulence" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<feTurbulence data-filterId="3" baseFrequency="0.02 0.03" result="turbulence" id="feturbulence" type="fractalNoise" numOctaves="1" seed="1"></feTurbulence>
<feDisplacementMap id="displacement" xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" in2="turbulence" scale="13" />
</filter>
</defs>
<g class="jellyfish" filter="url(#turbulence)">
<path class="tentacle"/>
<path class="tentacle"/>
<path class="tentacle" />
<path class="tentacle" />
<path class="tentacle"/>
<path class="tentacle"/>
<path class="tentacle"/>
<path class="tentacle"/>
<path class="tentacle"/>
<path class="face" />
<path class="outerJelly"/>
<path id="freckle" />
<path id="freckle"/>
<path id="freckle-4"/>
</g>
<g id="bubbles" fill="#fff">
<path class="bubble"/>
<path class="bubble"/>
<path class="bubble" />
<path class="bubble"/>
<path class="bubble"/>
<path class="bubble"/>
<path class="bubble" />
</g>
<g class="jellyfish face">
<path class="eye lefteye" fill="#b4bebf" d=""/>
<path class="eye righteye" fill="#b4bebf" d=""/>
<path class="mouth" fill="#d3d3d3" opacity=".72"/>
</g>
</svg>
- एसवीजी बनाने वाले रास्तों और रास्तों के समूह:
- स्पर्शक
- चेहरा - बूँद और बाहरी जेली
- आंखें - चेतन खुली और बंद
- बुलबुले - जेलिफ़िश के चारों ओर बेतरतीब ढंग से एनिमेट करें - आकार और अल्फा एनिमेट करें
- एम, एम : ले जाएँ
- एल, एल, एच, एच, वी, वी : लाइन टू
- C, c, S, s : क्यूबिक बेजियर कर्व टू
- क्यू, क्यू, टी, टी: द्विघात बेजियर वक्र
- ए, ए: अण्डाकार चाप वक्र को
- जेड, जेड - पथ बंद करें
- जेलिफ़िश को धीरे-धीरे ऊपर और नीचे जाना चाहिए
- जेलिफ़िश के क्लिक पर आँखें झपकनी चाहिए
- जेलिफ़िश के शरीर पर डगमगाने/शोर का प्रभाव होना चाहिए।
अब जब हम समझ गए हैं कि यह एसवीजी किस चीज से बना है, आइए कंपोज़ में स्थिर संस्करण को प्रस्तुत करने के बारे में जानें।
कस्टम इमेज वेक्टर बनाना
कंपोज़ में ImageVector की अवधारणा है , जहाँ आप SVG के समान प्रोग्रामेटिक रूप से एक वेक्टर बना सकते हैं। उन वैक्टर/एसवीजी के लिए जिन्हें आप बिना बदले रेंडर करना चाहते हैं, आप पेंटर रिसोर्स (R.drawable.vector_image) का उपयोग करके एक वेक्टर ड्रायबल भी लोड कर सकते हैं। यह इसे इमेजवेक्टर में परिवर्तित करने का ख्याल रखेगा जो कंपोज़ प्रस्तुत करेगा।
अब आप अपने आप से पूछ रहे होंगे - क्यों न केवल जेलीफ़िश को एक एसवीजी के रूप में एक एक्सएमएल फ़ाइल में आयात किया जाए और इसका उपयोग करके इसे लोड किया जाए painterResource(R.drawable.jelly_fish)
?
यह एक बड़ा सवाल है - और जेलीफ़िश को इस तरह से लोड करना संभव है, एसवीजी के अशांति पहलू को हटा दें और छवि एक्सएमएल लोड होने के साथ प्रस्तुत हो जाएगी (जैसा कि यहां प्रलेखन में समझाया गया है )। लेकिन हम पथ के अलग-अलग हिस्सों के साथ कुछ और करना चाहते हैं, जैसे कि क्लिक पर भागों को एनिमेट करना और शरीर पर शोर प्रभाव लागू करना, इसलिए हम ImageVector
प्रोग्रामेटिक रूप से अपना निर्माण करेंगे।
कंपोज़ में इस जेलीफ़िश को रेंडर करने के लिए, हम पथ डेटा (या d
पथ पर "" टैग) की प्रतिलिपि बना सकते हैं, जिससे मछली बनती है, उदाहरण के लिए, पहले स्पर्शक में निम्न पथ डेटा होता है:
M226.31 258.64c.77 8.68 2.71 16.48 1.55 25.15-.78 8.24-5 15.18-7.37 23-3.1 10.84-4.65 22.55 1.17 32.52 4.65 7.37 7.75 11.71 5.81 21.25-2.33 8.67-7.37 16.91-2.71 26 4.26 8.68 7.75 4.34 8.14-3 .39-12.14 0-24.28.77-36 .78-16.91-12-27.75-2.71-44.23 7-12.15 11.24-33 7.76-46.83z
अब आप शायद सोच रहे हैं - क्या मुझे अपने सिर में चित्र बनाना है और सभी पदों और आदेशों को हाथ से जानना है? नहीं, बिलकुल नहीं। आप अधिकांश डिजाइन कार्यक्रमों में एक वेक्टर बना सकते हैं - जैसे कि Figma या Inkscape, और अपने ड्राइंग के परिणाम को अपने लिए यह जानकारी प्राप्त करने के लिए एक SVG को निर्यात करें। वाह!
कंपोज़ में वेक्टर बनाने के लिए: हम कॉल करते हैं rememberVectorPainter
, जो एक बनाता है ImageVector
, और हम एक कॉल बनाते हैं Group
, jellyfish
फिर दूसरा Group
कॉल tentacles
करते हैं और हम Path
पहले टेंटकल के लिए इसके अंदर पहला स्थान रखते हैं। RadialGradient
हम पूरी जेलिफ़िश के लिए पृष्ठभूमि के रूप में भी सेट करते हैं।
और निम्नलिखित का परिणाम रेडियल ग्रेडिएंट बैकग्राउंड के साथ स्क्रीन पर खींचा गया एक छोटा टेंटकल है!
हम एसवीजी के सभी तत्वों के लिए इस प्रक्रिया को दोहराते हैं - एसवीजी फ़ाइल से पथ के बिट्स लेते हैं और रंग और अल्फा को उस पथ पर लागू करते हैं जो तैयार किया जाएगा, हम तार्किक रूप से पथों को स्पर्शक, चेहरे, में समूहित करते हैं। बुलबुले आदि:
अब हमारे पास उपरोक्त के साथ हमारी पूरी जेलीफ़िश रेंडरिंग है ImageVector
:
एनिमेटिंग इमेजवेक्टर पथ और समूह
हम इस वेक्टर के कुछ हिस्सों को एनिमेट करना चाहते हैं:
तो आइए देखें कि हम कैसे ImageVector
.
जेलिफ़िश को ऊपर और नीचे ले जाना
कोडपेन को देखते हुए, हम देख सकते हैं कि जेलिफ़िश अनुवाद के साथ ऊपर और नीचे (वाई अनुवाद) चल रहा है। रचना में ऐसा करने के लिए, हम एक अनंत संक्रमण और a बनाते हैं जो translationY
3000 मिली से अधिक एनिमेटेड होगा, फिर हम जेलिफ़िश वाले समूह को सेट करते हैं, और चेहरा एक होने के लिए translationY
, यह ऊपर और नीचे एनीमेशन का उत्पादन करेगा।
बढ़िया - का हिस्सा ImageVector
अब ऊपर और नीचे एनिमेट हो रहा है, आप देखेंगे कि बुलबुले उसी स्थिति में रहते हैं।
पलक झपकते ️
कोडपेन को देखते हुए, हम देख सकते हैं कि प्रत्येक आँख पर एक scaleY
और opacity
एनीमेशन है। आइए इन दो वेरिएबल्स को बनाते हैं और स्केल को Group
और अल्फा पर लागू करते हैं Path
। हम इसे केवल जेलीफ़िश के क्लिक पर भी लागू करेंगे, ताकि इसे और अधिक संवादात्मक एनीमेशन बनाया जा सके।
हम दो एनिमेटेबल बनाते हैं जो एनीमेशन स्थिति को बनाए रखेंगे, और एक सस्पेंड फ़ंक्शन जिसे हम जेलिफ़िश पर क्लिक करने पर कॉल करेंगे - हम इन गुणों को स्केल करने और आंखों को धुंधला करने के लिए एनिमेट करते हैं।
अब हमारे पास क्लिक पर एक प्यारा ब्लिंकिंग एनीमेशन है - और हमारी जेलिफ़िश लगभग पूरी हो चुकी है!
विरूपण/शोर प्रभाव लागू करना
इसलिए हमारे पास अधिकांश चीजें हैं जिन्हें हम एनिमेटेड करना चाहते हैं - ऊपर और नीचे की गति, और पलक झपकना। आइए देखें कि जेलिफ़िश के शरीर पर किस तरह से लड़खड़ाहट का प्रभाव पड़ता है, शरीर और स्पर्शक उस पर शोर के साथ चलते हैं ताकि उसे उस पर गति का आभास हो सके।
एसवीजी और एनीमेशन कोड को देखते हुए, हम देख सकते हैं कि यह feTurbulence
शोर उत्पन्न करने के लिए उपयोग करता है जिसे बाद में एसवीजी पर लागू किया जाता है feDisplacementMap
।
<filter id="turbulence" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<feTurbulence data-filterId="3" baseFrequency="0.02 0.03" result="turbulence" id="feturbulence" type="fractalNoise" numOctaves="1" seed="1"></feTurbulence>
<feDisplacementMap id="displacement" xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" in2="turbulence" scale="13" />
</filter>
</defs>
<g class="jellyfish" filter="url(#turbulence)">
इसे प्राप्त करने के लिए हम AGSL शेडर्स का उपयोग कर सकते हैं , यह ध्यान देने योग्य है कि यह केवल Tiramisu और up (API 33+) पर समर्थित है। सबसे पहले हमें एक शेडर बनाने की जरूरत है जो एक डगमगाने का काम करेगा, हम पहले शोर का उपयोग नहीं करेंगे - सादगी के बजाय सिर्फ एक मैपिंग फ़ंक्शन।
जिस तरह से शेडर्स काम करते हैं वह यह है कि वे अलग-अलग पिक्सेल पर कार्य करते हैं - हमें एक निर्देशांक ( fragCoord
) मिलता है और हमसे एक रंग परिणाम उत्पन्न करने की उम्मीद की जाती है जो उस समन्वय पर प्रस्तुत किया जाएगा। नीचे प्रारंभिक शेडर है जिसका उपयोग हम कंपोज़ेबल को बदलने के लिए करेंगे:
हमारे मामले में, हम जिस इनपुट का उपयोग करेंगे, वह स्क्रीन पर हमारे वर्तमान में प्रदान किए गए पिक्सेल हैं। हम इसे उस चर के माध्यम से एक्सेस करते uniform shader contents;
हैं जिसे हम इनपुट के रूप में भेजेंगे। हम इनपुट निर्देशांक ( fragCoord
) लेते हैं, और हम इस निर्देशांक पर कुछ परिवर्तन लागू करते हैं - इसे समय के साथ आगे बढ़ाते हैं और आम तौर पर इसे स्थानांतरित करने के लिए इस पर कुछ गणित का प्रदर्शन करते हैं।
यह एक नया समन्वय पैदा करता है, इसलिए fragCoord
स्थिति पर सटीक रंग वापस करने के बजाय, हम उस स्थान को बदलते हैं जहां से हमें इनपुट पिक्सेल मिलता है। उदाहरण के लिए, यदि हमारे पास return contents.eval(fragCoord)
होता, तो इससे कोई परिवर्तन नहीं होता—यह पास-थ्रू होता। अब हमें कंपोज़ेबल के एक अलग बिंदु से पिक्सेल रंग मिलता है - जो कंपोज़ेबल की सामग्री पर एक अजीब विरूपण प्रभाव पैदा करेगा।
हमारे कंपोजेबल पर इसका उपयोग करने के लिए, हम इस शेडर को RenderEffect
कंपोजेबल की सामग्री के रूप में लागू कर सकते हैं:
हम इनपुट createRuntimeShaderEffect
के रूप में पासिंग का उपयोग करते हैं। WOBBLE_SHADER
यह कंपोज़ेबल की वर्तमान सामग्री लेता है, और इसे पैरामीटर नाम " contents
" के साथ शेडर में इनपुट के रूप में प्रदान करता है। फिर हम अंदर की सामग्री को क्वेरी करते हैं WOBBLE_SHADER
। time
चर समय के साथ लड़खड़ाहट को बदल देता है (एनीमेशन बनाते हुए) ।
इसे चलाते हुए, हम देख सकते हैं कि पूरा Image
अब विकृत हो गया है और थोड़ा और लड़खड़ाता हुआ दिखता है - बिल्कुल जेलिफ़िश की तरह।
अगर हम चेहरे और बुलबुले पर प्रभाव लागू नहीं करना चाहते हैं, तो हम उन्हें अलग-अलग निकाल सकते हैं ImageVectors
, और उन वैक्टरों पर रेंडर प्रभाव लागू करने पर छोड़ सकते हैं:
शोर प्रभाव लागू करना
जिस शेडर को हमने ऊपर निर्दिष्ट किया है, वह कंपोज़ेबल की सामग्री पर विस्थापन लागू करने के लिए नॉइज़ फ़ंक्शन का उपयोग नहीं कर रहा है। शोर एक अधिक संरचित यादृच्छिक फ़ंक्शन के साथ विस्थापन को लागू करने का एक तरीका है। इस तरह का एक शोर है पर्लिन शोर (जो कि feTurbulence
हुड के नीचे उपयोग किया जाता है), अगर हम पर्लिन शोर फ़ंक्शन चलाने के परिणाम प्रस्तुत करते हैं तो यह ऐसा दिखाई देगा:
हम अंतरिक्ष में प्रत्येक समन्वय के लिए शोर मान का उपयोग करते हैं, और " contents
" शेडर में एक नए समन्वय को क्वेरी करने के लिए इसका उपयोग करते हैं।
आइए अपने शेडर को पर्लिन नॉइज़ फंक्शन ( इस गिथब रेपो से अनुकूलित ) का उपयोग करने के लिए अपडेट करें। फिर हम इसका उपयोग इनपुट निर्देशांक से आउटपुट निर्देशांक (अर्थात विस्थापन मानचित्र) तक समन्वय मानचित्रण निर्धारित करने के लिए करेंगे।
इस शोर समारोह को लागू करने से हमें बहुत बेहतर परिणाम मिलते हैं! जेलिफ़िश ऐसा दिखता है जैसे वह पानी के अंदर घूम रहा हो।
लेकिन मैं इसका इस्तेमाल क्यों करूं?
इस बिंदु पर आप सोच रहे होंगे, यह अच्छा है - लेकिन इसके उपयोग के मामले में बहुत आला, रेबेका। ज़रूर - शायद आप हर दिन काम पर एक एनिमेटेड जेलिफ़िश नहीं बना रहे हैं (हम सही सपना देख सकते हैं?) लेकिन RenderEffects
किसी भी रचना योग्य पेड़ पर लागू किया जा सकता है - जिससे आप अपनी इच्छित किसी भी चीज़ पर प्रभाव लागू कर सकते हैं।
उदाहरण के लिए, आप अपने ग्रेडिएंट टेक्स्ट या पूरी कंपोजेबल स्क्रीन को शोर प्रभाव या किसी अन्य एजीएसएल प्रभाव को अपनी दिल की इच्छाओं पर क्यों नहीं लेना चाहेंगे?
लपेटो
इसलिए हमने इस ब्लॉग पोस्ट में कई दिलचस्प अवधारणाओं को शामिल किया है - ImageVectors
SVG से कस्टम बनाना, a के कुछ हिस्सों को एनिमेट करना ImageVector
और AGSL शेडर्स को RenderEffects
कंपोज़ में हमारे UI के रूप में लागू करना।
जेलिफ़िश के पूरे कोड के लिए — पूरा सार यहाँ देखें । AGSL RenderEffects के बारे में अधिक जानकारी के लिए - प्रलेखन देखें, या इसके अन्य उदाहरण के उपयोग के लिए JetLagged नमूना देखें ।
यदि आपके कोई प्रश्न हैं — तो बेझिझक मास्टोडॉन androiddev.social/@riggaroo या Twitter पर संपर्क करें ।
इस पोस्ट पर मूल्यवान प्रतिक्रिया के लिए जोलांडा वेरहोफ , निक बुचर , फ्लोरिना मुंटेनेस्कु , रोमेन गाइ , नादेर जवाद का धन्यवाद ।