कंटेंट पर जाएँ
charmotion

Astro में Rive: Islands, client:visible और Lazy WASM

Charmotion टीम द्वारा · अपडेट:

frameworks और no-code गाइड का कवर इलस्ट्रेशन: Astro में Rive: Islands, client:visible और Lazy WASM, Charmotion के डेमो कैरेक्टर के साथ

Astro में Rive इस्तेमाल करने के लिए React की ज़रूरत नहीं है। एक canvas और एक <script> जोड़ें जो आधिकारिक web runtime @rive-app/canvas को import करे, या अपने मौजूदा React, Vue या Svelte मैस्कॉट component को client:visible के साथ island के रूप में hydrate करें। दोनों तरीके Rive के WASM को critical path से दूर रखते हैं, और दोनों में कैरेक्टर को एक ही तरह की view model calls से कंट्रोल किया जाता है।

मुख्य बातें

  • Astro में कोई Rive integration नहीं है; web runtime एक सामान्य <script> में काम करता है।
  • Astro <script> tags को bundle और process करता है, इसलिए npm से import काम करता है।
  • client:visible किसी island को तब hydrate करता है जब वह viewport में आता है; client:only server rendering को छोड़ देता है।
  • fold के नीचे वाले मैस्कॉट को लेज़ी तरीके से शुरू करने के लिए plain scripts में IntersectionObserver का इस्तेमाल करें।
  • canvas तैयार होने तक poster image दिखाएँ, और prefers-reduced-motion का ध्यान रखें।

क्या Astro में Rive इस्तेमाल करने के लिए React ज़रूरी है?

नहीं। Astro डिफ़ॉल्ट रूप से शून्य JavaScript भेजता है और इसमें कोई Rive integration नहीं है, पर इसकी ज़रूरत भी नहीं है। Rive का web runtime (@rive-app/canvas या @rive-app/webgl2) सामान्य JavaScript है जो canvas में ड्रॉ करता है। आप इसे बिना किसी UI framework के, Astro के <script> tag से चला सकते हैं।

आपके पास पहले से जो है, उसके हिसाब से तरीका चुनें:

स्थितितरीका
content site, कोई UI framework नहींcanvas और .astro फ़ाइल में एक <script>
आपके पास पहले से @rive-app/react-canvas वाला React मैस्कॉट हैइसे client:visible के साथ island के रूप में render करें
Vue या Svelte componentउस framework के integration के साथ वही island तरीका

plain script सबसे हल्का विकल्प है: कोई framework runtime नहीं भेजा जाता, सिर्फ़ Rive का JavaScript और उसका WASM। island तब सही है जब मैस्कॉट दूसरे interactive components के साथ state शेयर करता हो। ये components React गाइड, Vue गाइड और Svelte गाइड में बताए गए हैं।

.astro फ़ाइल में plain script से Rive कैसे जोड़ें?

npm install @rive-app/canvas से runtime इंस्टॉल करें और फ़ाइल को public/ में रखें। Astro डिफ़ॉल्ट रूप से <script> tags को process करता है: यह imports को bundle करता है, एक module बनाता है और उसे हर पेज पर एक ही बार शामिल करता है, भले ही component कई बार दिखे।

---
// src/components/Mascot.astro
---
<canvas class="mascot" width="320" height="320"
        role="img" aria-label="Animated mascot"></canvas>

<script>
  import { Rive } from "@rive-app/canvas";

  for (const canvas of document.querySelectorAll("canvas.mascot")) {
    const io = new IntersectionObserver(([entry]) => {
      if (!entry.isIntersecting) return;
      io.disconnect();
      const r = new Rive({
        src: "/character.riv",
        canvas,
        stateMachine: "CharacterViewModelMachine",
        autoplay: true,
        autoBind: true,
        onLoad: () => {
          r.resizeDrawingSurfaceToCanvas();
          r.viewModelInstance.number("mood").value = 2; // wave
        },
      });
      canvas.addEventListener("click", () =>
        r.viewModelInstance.trigger("success").trigger());
    });
    io.observe(canvas);
  }
</script>

क्योंकि script हर पेज पर एक बार चलता है, यह किसी एक element को मानकर चलने के बजाय हर मैस्कॉट canvas पर loop करता है। observer runtime शुरू होने और .riv डाउनलोड, दोनों को तब तक टालता है जब तक कैरेक्टर स्क्रॉल होकर स्क्रीन पर न आ जाए।

अगर आप Astro के view transitions (<ClientRouter /> component) इस्तेमाल करते हैं, तो client-side navigation पर scripts दोबारा नहीं चलते। setup को astro:page-load event के listener में रखें, और पेज बदलने से पहले r.cleanup() कॉल करें।

client:visible और client:only में से कब क्या इस्तेमाल करें?

Astro के directives तय करते हैं कि framework component कब hydrate होगा। मैस्कॉट island के लिए तीन ज़रूरी हैं:

Directiveकब hydrate होता हैकिसके लिए
client:loadपेज लोड होते ही तुरंतfold के ऊपर दिखने वाला हीरो मैस्कॉट
client:visibleजब component viewport में आता हैपेज पर नीचे वाले मैस्कॉट
client:only="react"सिर्फ़ client पर; कोई server-rendered HTML नहींऐसे components जो import या render के दौरान window को छूते हैं
---
import Mascot from "../components/Mascot.jsx";
---
<Mascot client:visible mood={0} />

Astro के docs के अनुसार client:visible अंदर से IntersectionObserver इस्तेमाल करता है, इसलिए पिछले सेक्शन वाला lazy start आपको खुद लिखे बिना मिल जाता है। यह rootMargin option भी लेता है, ताकि component के स्क्रॉल होकर दिखने से थोड़ा पहले hydration शुरू हो जाए।

client:only का इस्तेमाल तभी करें जब server rendering टूट रहा हो, क्योंकि यह server HTML को पूरी तरह हटा देता है और JavaScript चलने तक वह जगह खाली रहती है। अच्छी तरह लिखा गया Rive component instance को effect या mount hook में बनाता है, इसलिए वह server पर ठीक से render होता है और client:visible के साथ काम करता है।

LCP पर असर डाले बिना .riv और rive.wasm कैसे लोड करें?

पहला instance बनने पर web runtime rive.wasm को unpkg से डाउनलोड करता है। हीरो मैस्कॉट पर यह request, JavaScript और .riv के साथ मिलकर, आपके largest contentful paint से होड़ करती है। तीन आदतें इसे काबू में रखती हैं:

  • canvas को LCP element न बनने दें। उसी जगह पर तय dimensions वाली एक static poster image render करें। headline या poster LCP candidate बन जाता है, और तैयार होने पर एनिमेशन poster की जगह ले लेता है।
  • self-host और preload सिर्फ़ fold के ऊपर करें। Rive के docs अपनी copy serve करने के लिए RuntimeLoader.setWasmUrl() दिखाते हैं, जिसका वर्ज़न इंस्टॉल किए गए package से मेल खाना चाहिए। हीरो कैरेक्टर के लिए .riv का <link rel="preload"> जोड़ें ताकि वह साथ-साथ लोड हो।
  • बाकी सब कुछ लेज़ी तरीके से शुरू करें। fold के नीचे client:visible या observer pattern इस्तेमाल करें, ताकि ज़रूरत पड़ने तक कुछ भी डाउनलोड न हो।
import { RuntimeLoader } from "@rive-app/canvas";
import riveWasmUrl from "@rive-app/canvas/rive.wasm?url";

// Run once, before the first new Rive(...)
RuntimeLoader.setWasmUrl(riveWasmUrl);

Astro, Vite इस्तेमाल करता है, इसलिए ?url import wasm को hashed नाम के साथ आपके build output में कॉपी कर देता है। जब पेज पर कई एनिमेशन हों, तो Rive @rive-app/canvas की सलाह देता है, क्योंकि यह browser की WebGL context सीमा से बंधा नहीं है।

एक वेबसाइट मॉकअप: बाईं ओर हेडलाइन और हीरो में Rive फ़ाइल से चलता एनिमेटेड मैस्कॉट Kiko
वेबसाइट के हीरो सेक्शन में मैस्कॉट: हेडलाइन के बगल में canvas में एक छोटी Rive फ़ाइल।

एनिमेशन लोड होने से पहले static poster कैसे दिखाएँ?

एक इमेज और canvas को एक ही box में एक के ऊपर एक रखें, फिर Rive का पहला फ़्रेम ड्रॉ होते ही इमेज छिपा दें। canvas का साइज़ पहले से सेट करने से लेआउट शिफ़्ट नहीं होता।

<div class="mascot-box">
  <img src="/character-poster.png" width="320" height="320" alt="">
  <canvas class="mascot" width="320" height="320"
          role="img" aria-label="Animated mascot"></canvas>
</div>

<style>
  .mascot-box { position: relative; width: 320px; aspect-ratio: 1; }
  .mascot-box > * { position: absolute; inset: 0; width: 100%; height: 100%; }
  .mascot-box.ready img { display: none; }
</style>

onLoad callback में class जोड़ें: canvas.parentElement.classList.add("ready")। इमेज कैरेक्टर का idle फ़्रेम होनी चाहिए, ताकि बदलाव दिखाई न दे।

Charmotion इस पैटर्न के लिए ज़रूरी हिस्से देता है: एक कैरेक्टर या लोगो इमेज से कुछ ही मिनटों में 17 एनिमेशन, एक Character व्यू मॉडल (0 से 9 तक एक mood नंबर और छह ट्रिगर) वाली एक .riv में पैक, data binding वाला एक वेब index.html उदाहरण, और सोशल और ईमेल के लिए ज़रूरत पर MP4, GIF और ट्रांसपेरेंट WebM और MOV एक्सपोर्ट।

Astro में prefers-reduced-motion का ध्यान कैसे रखें?

अपने script में media query जाँचें और या तो runtime को पूरी तरह छोड़ दें या उसे paused स्थिति में शुरू करें।

const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!reduce) {
  io.observe(canvas);   // start the animation as usual
}
// otherwise the poster image stays visible

content sites पर runtime छोड़ना बेहतर विकल्प है: जिन visitors ने कम मोशन चाहा है, वे कम डाउनलोड भी करते हैं। अगर कैरेक्टर कोई मतलब बताता है, जैसे कोई status, तो उसे रखें लेकिन autoplay: false पास करें, ताकि runtime एक स्थिर फ़्रेम ड्रॉ करे और आप फिर भी मूड सेट कर सकें।

इस media feature की दो वैल्यू हैं, no-preference और reduce, और यह visitor के operating system की सेटिंग दिखाता है। कौन सा मोशन रखना है, यह reduced motion गाइड में बताया गया है।

islands के लिए यही जाँच framework component के अंदर, उस hook में करें जो Rive instance बनाता है, ताकि दोनों तरीके एक जैसा व्यवहार करें।

Astro पेज में Rive मैस्कॉट कैसे जोड़ें

  1. रनटाइम इंस्टॉल करें. npm install @rive-app/canvas चलाएँ और .riv व एक poster image को public/ में रखें।
  2. Markup जोड़ें. एक component बनाएँ जिसमें तय साइज़ वाला canvas और poster image एक ही box में एक के ऊपर एक हों।
  3. script जोड़ें. <script> में Rive import करें और उसे stateMachine: "CharacterViewModelMachine" और autoBind: true के साथ बनाएँ।
  4. लेज़ी तरीके से शुरू करें. IntersectionObserver इस्तेमाल करें, या framework component को client:visible के साथ render करें।
  5. स्टेट चलाएँ. viewModelInstance.number("mood").value सेट करें और event listeners से ट्रिगर फ़ायर करें।
  6. मोशन कम करें सेटिंग का ध्यान रखें. जब prefers-reduced-motion: reduce मैच करे, तो runtime को स्किप या पॉज़ करें।

अक्सर पूछे जाने वाले सवाल

मेरा Rive script सिर्फ़ पहले मैस्कॉट के लिए ही क्यों चलता है?

Astro किसी processed script को हर पेज पर एक ही बार शामिल करता है, चाहे component कितनी भी बार आए। उदाहरण की तरह, getElementById की जगह querySelectorAll से सभी मैच होने वाले canvas चुनें और उन पर लूप चलाएँ। इससे हर canvas को उसका अपना Rive instance मिलता है।

क्या Astro view transition के बाद भी Rive काम करता रहता है?

अपने आप नहीं। view transitions चालू होने पर client-side navigation में processed scripts दोबारा नहीं चलतीं, इसलिए नए पेजों पर खाली canvas दिखता है। अपना setup astro:page-load event पर चलाएँ, जो पहले load पर और हर navigation के बाद फ़ायर होता है, और swap से पहले पुराने instances को साफ़ करें।

क्या मैं Rive island के लिए client:idle इस्तेमाल कर सकता हूँ?

हाँ। client:idle तब hydrate करता है जब ब्राउज़र idle हो जाता है, जो ऐसे मैस्कॉट के लिए सही है जो load पर दिखता है पर ज़रूरी नहीं है। fold के नीचे की किसी भी चीज़ के लिए client:visible आमतौर पर बेहतर है, क्योंकि यह तब तक इंतज़ार करता है जब तक विज़िटर सच में कैरेक्टर तक स्क्रॉल न कर ले।

इस गाइड से झटपट जवाब

स्रोत

संबंधित गाइड