हीरो एनिमेशन और Core Web Vitals: LCP, CLS और INP को ग्रीन रखें
Charmotion टीम द्वारा · अपडेट:

एनिमेटेड हीरो से Core Web Vitals को नुकसान होना ज़रूरी नहीं, अगर पेज कभी एनिमेशन का इंतज़ार न करे। एक स्थिर पोस्टर इमेज को LCP एलिमेंट बनाएँ, width और height या aspect-ratio से हीरो का साइज़ पहले से तय करें, और एनिमेशन runtime लोड के बाद शुरू करें, सिर्फ़ तब जब हीरो दिख रहा हो और reduced motion बंद हो।
मुख्य बातें
- Canvas, LCP का candidate नहीं है, इसलिए Rive, Lottie canvas या three.js हीरो, LCP को आपकी पोस्टर इमेज या हेडलाइन पर छोड़ देता है। उस पोस्टर को छोटा, HTML में खोजे जाने लायक और high priority वाला बनाएँ।
- वीडियो के लिए, LCP पोस्टर का लोड समय या पहला फ़्रेम, जो भी पहले हो, इस्तेमाल करता है, इसलिए हल्का पोस्टर सीधे मदद करता है।
- Layout shift गायब dimensions से आता है: हर img, video और canvas container को width और height या aspect-ratio दें, और top या left नहीं, transform एनिमेट करें।
- जब लोड के दौरान runtime कोड चलता है, तो INP बिगड़ता है। runtime को idle पर या हीरो के स्क्रॉल होकर दिखने पर लोड करें, और उसके स्क्रॉल होकर दूर जाने पर रोक दें।
- prefers-reduced-motion में पोस्टर रखें और runtime लोड करना ही छोड़ दें।
पहले से कोई कैरेक्टर या लोगो है? असली ऐप स्क्रीन में उदाहरण देखें: स्प्लैश, ऑनबोर्डिंग, लोडिंग और एम्प्टी स्टेट स्क्रीन, और हर एक में चलने वाला एनिमेशन।
क्या हीरो एनिमेशन LCP, CLS या INP को नुकसान पहुँचाता है?
यह तीनों को नुकसान पहुँचा सकता है, हर एक को अलग तरह से। Google के Core Web Vitals असली पेज लोड के 75वें percentile पर मापे जाते हैं, और हर एक का साफ़ लक्ष्य है:
| मेट्रिक | अच्छा | हीरो एनिमेशन इसे कैसे नुकसान पहुँचाता है | समाधान |
|---|---|---|---|
| Largest Contentful Paint (LCP) | 2.5 s या कम (4.0 s से ऊपर खराब) | भारी GIF, वीडियो या देर से लोड होने वाली इमेज सबसे बड़ा एलिमेंट बनकर देर से आती है | LCP एलिमेंट के रूप में एक छोटी पोस्टर इमेज, high fetch priority, कभी lazy-load नहीं |
| Cumulative Layout Shift (CLS) | 0.1 या कम | Canvas या वीडियो बिना तय जगह के दिखता है और हेडलाइन को नीचे धकेल देता है | width और height attributes या aspect-ratio; transform एनिमेट करें |
| Interaction to Next Paint (INP) | 200 ms या कम (500 ms से ऊपर खराब) | Runtime JavaScript, WebAssembly और सीन सेटअप main thread पर चलते हैं, जबकि यूज़र टैप करने की कोशिश कर रहा होता है | Runtime को idle पर या दिखने पर शुरू करें; ऑफ़-स्क्रीन होने पर रोकें |
web.dev, INP को किसी विज़िट के दौरान "सभी click, tap और keyboard interactions" की latency बताता है, और बताता है कि input delay "main thread पर लंबे tasks" से हो सकता है। जो हीरो उस समय 3D सीन बूट करता है जब विज़िटर मेन्यू बटन टैप कर रहा हो, वह वहाँ पहुँचने का किताबी तरीका है।
अच्छी खबर: इनमें से किसी के लिए भी स्थिर पेज ज़रूरी नहीं। ज़रूरी है events का एक क्रम। पहले एक स्थिर फ़्रेम पेंट करें, उसका box तय रखें, फिर ब्राउज़र के पास समय होने पर मोशन शुरू होने दें।
एनिमेटेड हीरो पर LCP एलिमेंट क्या होना चाहिए?
हीरो के पहले या आराम वाले फ़्रेम की एक स्थिर इमेज, एनिमेशन जितने ही साइज़ की। web.dev उन एलिमेंट टाइप को लिस्ट करता है जिन्हें LCP गिनता है: <img>, SVG के अंदर <image>, <video>, CSS url() बैकग्राउंड इमेज वाले एलिमेंट, और टेक्स्ट वाले block एलिमेंट। <canvas> उस लिस्ट में नहीं है, जो Rive, Lottie (canvas renderer) और three.js हीरो के लिए मायने रखता है: canvas कभी LCP एलिमेंट नहीं बनता, इसलिए आपकी हेडलाइन या पोस्टर बनता है।
हर फ़ॉर्मेट का समय कैसे मापा जाता है:
- वीडियो: LCP "the poster image load time or first frame presentation time for videos" में से जो पहले हो, उसका इस्तेमाल करता है। हल्का poster जीतता है।
- GIF और animated PNG: पहले फ़्रेम का presentation time इस्तेमाल होता है, लेकिन ब्राउज़र को उसे दिखाने के लिए बड़ी फ़ाइल का काफ़ी हिस्सा फिर भी डाउनलोड करना पड़ता है।
- Canvas runtimes: गिने नहीं जाते, इसलिए LCP canvas के नीचे वाली poster इमेज या सबसे बड़े टेक्स्ट ब्लॉक को मापता है।
फिर उस poster को किसी भी LCP इमेज की तरह लोड करें। web.dev की सलाह सीधी है: "Never lazy-load your LCP image", resource को "discoverable from the HTML source" बनाएँ, और उस पर fetchpriority="high" सेट करें (लेकिन एक या दो से ज़्यादा इमेज पर नहीं)। Google Flights के एक टेस्ट में Fetch Priority ने LCP को 2.6 s से 1.9 s कर दिया। <video> poster का अपना कोई fetchpriority attribute नहीं होता, इसलिए अगर poster आपका LCP एलिमेंट है, तो उसे high priority के साथ preload करें:
<link rel="preload" as="image" href="/hero-poster.webp" fetchpriority="high">
<div class="hero-art">
<img class="hero-poster" src="/hero-poster.webp" width="960" height="960"
alt="Hushwave logo" fetchpriority="high">
<canvas class="hero-canvas" width="960" height="960" aria-hidden="true" hidden></canvas>
</div>
किसी एक इमेज के लिए इनमें से एक ही तरीका इस्तेमाल करें: ऊपर वाला <img> पहले से HTML में है, इसलिए preload लाइन की ज़रूरत तभी है जब poster कोई video poster या CSS background हो।
एनिमेटेड hero को layout shift पैदा करने से कैसे रोकें?
कुछ भी लोड होने से पहले बॉक्स रिज़र्व करें, और एनिमेशन को उसके अंदर रखें। web.dev का नियम है: "Always include width and height size attributes on your images and video elements", और जब ऐसा न हो सके, तो जगह "CSS aspect-ratio or similar" से रिज़र्व करें। Script से बने canvas का अपना कोई साइज़ तब तक नहीं होता जब तक runtime उसे सेट न करे, इसलिए उसके container को साइज़ दें।
.hero-art {
position: relative;
width: min(100%, 480px);
aspect-ratio: 1 / 1;
}
.hero-art > * {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
चूँकि poster और canvas एक ही बॉक्स में एक के ऊपर एक रखे हैं, एक को दूसरे से बदलने पर लेआउट नहीं बदलता। तीन और आदतें CLS को ज़ीरो पर रखती हैं:
- Transforms को एनिमेट करें। web.dev transform एनिमेशन (translate, scale, rotate, skew) को सुरक्षित बताता है और चेतावनी देता है कि "changing the top and left properties also cause layout shifts." यही बात उस लोगो पर भी लागू होती है जो अपनी width बदलकर बड़ा होता है।
- Overshoot को बॉक्स के अंदर रखें। जो मैस्कॉट कूदता या दबता है, उसकी कूद की ऊँचाई उसके container के अंदर होनी चाहिए, वरना वह कट जाएगा। Artboard का साइज़ सबसे बड़े pose के हिसाब से रखें, resting pose के हिसाब से नहीं।
- Hero के ऊपर देर से आने वाले banners नहीं। Cookie bar या announcement जो paint होने के बाद hero को नीचे धकेल दे, वह भी layout shift है, hero में चाहे जो भी हो।
INP को नुकसान पहुँचाए बिना animation runtime कैसे लोड करें?
इसे पेज लोड के अहम हिस्से के दौरान शुरू न करें। web.dev बताता है कि "one thing that can extend an interaction's input delay while a page loads is script evaluation", और animation runtime में काफ़ी script होता है: Rive runtime में एक WebAssembly module होता है, Lottie player JSON को shapes में parse करता है, और three.js सीन shaders compile करके textures upload करता है।
एक पैटर्न जो इन सभी के लिए काम करता है: hero के स्क्रीन पर आने तक इंतज़ार करें (Intersection Observer API "deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result" के लिए ही है), फिर किसी idle पल का इंतज़ार करें, फिर runtime को import करें।
const box = document.querySelector(".hero-art");
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
function whenIdle(fn) {
if ("requestIdleCallback" in window) requestIdleCallback(fn, { timeout: 2000 });
else setTimeout(fn, 200);
}
async function startHero() {
const { Rive } = await import("@rive-app/canvas-lite");
const canvas = box.querySelector(".hero-canvas");
const r = new Rive({
src: "/hero.riv",
canvas,
stateMachine: "State Machine 1",
autoplay: true,
onLoad: () => {
r.resizeDrawingSurfaceToCanvas();
canvas.hidden = false;
box.querySelector(".hero-poster").hidden = true;
},
});
}
if (!reduce) {
new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
observer.disconnect();
whenIdle(startHero);
}
}).observe(box);
}
बारीकियों पर नोट्स:
requestIdleCallbackBaseline नहीं है (MDN इसे "Limited availability" बताता है), इसलिए samplesetTimeoutपर fallback करता है। MDN उस काम के लिएtimeoutकी भी सलाह देता है जो होना ही चाहिए, इसीलिए इसे 2 सेकंड पर सेट किया गया है।stateMachine(singular) वह parameter है जिसे Rive ने runtime v2.41.0 में जोड़ा; pluralstateMachinesअब भी काम करता है लेकिन deprecated है।- अगर आप
rive.wasmको preload करते हैं, तो Rive के docs चेतावनी देते हैं कि preload URL औरsetWasmUrl()को दिया गया URL "must be exactly the same", वरना फ़ाइल दो बार डाउनलोड होगी। Preloading आपकी LCP इमेज से भी होड़ करती है, इसलिए इसे तभी करें जब hero ही विज़िट की वजह हो। - Offscreen होने पर pause करें। Hero शुरू होने के बाद भी उसे observe करें, और viewport से बाहर जाने पर
r.pause()और लौटने परr.play()call करें। Rive state machines भी "settle" होती हैं, यानी कुछ न बदलने पर काम रोक देती हैं। three.js के लिएrenderer.setAnimationLoop(null)render loop रोक देता है; फिर से शुरू करने के लिए अपना loop function दोबारा pass करें। - वीडियो को इसकी कम ज़रूरत होती है। Decoding ब्राउज़र की media pipeline में चलती है, इसलिए muted
<video>का खर्च ज़्यादातर bandwidth है, script नहीं। web.dev का lazy-loading पैटर्नpreload="none"के साथ एक Intersection Observer इस्तेमाल करता है जो वीडियो के viewport में आने पर playback शुरू करता है।
हर hero animation format कितना भारी है?
जब एनिमेशन LCP एलिमेंट न हो, तब भी वज़न मायने रखता है: यह पेज की बाकी हर चीज़ से bandwidth के लिए होड़ करता है, और मोबाइल डेटा पर यह विज़िटर का पैसा है। ये हमारे animation file size benchmark (25 अक्टूबर 2026 को मापे गए) के अपने आँकड़े हैं, एक ही कैरेक्टर के एक 4-सेकंड idle loop के लिए, transfer के रूप में:
| फ़ॉर्मेट | फ़्लैट मैस्कॉट | चमकदार 3D लोगो | ट्रांसपेरेंट |
|---|---|---|---|
| MP4 (H.264, 1080 px) | 211 KB | 558 KB | No |
| Rive runtime (373 KB brotli, हर साइट पर एक बार) + .riv | लगभग 385 KB | लगभग 1.25 MB | Yes |
| Transparent WebM (VP9 alpha, 1080 px) | 500 KB | 2.0 MB | Yes |
| Transparent HEVC MOV (1080 px) | 743 KB | 1.1 MB | हाँ (Safari) |
| GIF (480 px) | 698 KB | 4.4 MB | सिर्फ़ 1-bit |
इससे क्या सीखें:
- GIF सबसे खराब hero format है। web.dev के अपने उदाहरण में एक ही क्लिप का GIF 3.7 MB था, जबकि MP4 551 KB और WebM 341 KB।
- Solid background पर opaque hero: MP4 सबसे हल्की फ़ाइल है।
- कई स्टेट या pointer interaction: Rive, क्योंकि हर अतिरिक्त स्टेट एक और वीडियो के बजाय एक ही फ़ाइल के अंदर कुछ किलोबाइट है। Rive का
canvas-litepackage सबसे छोटा variant है; इसमें text, layout, audio, scripting और Vector Feathering नहीं होते। - Lottie और three.js: हमने इन्हें नहीं मापा है, और दोनों काफ़ी हद तक आपके build पर निर्भर हैं (कौन सा player, कौन से three.js modules, model और textures कितने बड़े हैं)। Ship करने से पहले अपने ब्राउज़र के network panel में transferred साइज़ जाँच लें।
Charmotion के कैरेक्टर सेट में state machine वाली Rive फ़ाइल, MP4, GIF, transparent WebM और MOV, और हर एनिमेशन का एक still poster मिलता है, जो ठीक वही poster है जिसकी ज़रूरत ऊपर वाले LCP पैटर्न में है। इसके लोगो एनिमेशन pointer पर रिएक्ट करने वाला website hero बन सकते हैं, copy-paste वाले web code के साथ। देखें यह कैसे काम करता है।
Reduced motion चालू होने पर hero में क्या दिखना चाहिए?
Poster, और कुछ नहीं। MDN prefers-reduced-motion को वह सेटिंग बताता है जो ब्राउज़र को बताती है कि यूज़र "prefers an interface that removes, reduces, or replaces motion-based animations." यह जनवरी 2020 से सभी ब्राउज़र में उपलब्ध है, इसलिए इसे छोड़ने की कोई वजह नहीं है।
ऊपर वाली script runtime लोड करने से पहले ही इसे जाँचती है, जिससे डाउनलोड भी बचता है। Video hero के लिए autoplay attribute हटा दें और playback को script से तभी शुरू करें जब reduced motion बंद हो। CSS में:
@media (prefers-reduced-motion: reduce) {
.hero-canvas, .hero-video { display: none; }
.hero-poster { display: block; }
}
सेटिंग चाहे जो हो, हर hero loop पर accessibility के दो नियम लागू होते हैं:
- पाँच सेकंड से लंबे loops जो अपने आप शुरू होते हैं और दूसरे कंटेंट के साथ दिखते हैं, उन्हें pause, stop या hide करने का तरीका चाहिए (WCAG 2.2 SC 2.2.2, Level A)। जो hero एक बार चलकर रुक जाता है, वह इस समस्या से बच जाता है।
- किसी भी एक सेकंड में तीन बार से ज़्यादा flashing नहीं (SC 2.3.1)। 3D hero में तेज़ light sweeps और चमकते particles आम तौर पर इसकी वजह होते हैं।
पूरा पैटर्न, जिसमें Rive या video loop को still pose से बदलना भी शामिल है, accessible animation और reduced motion में है।
कैसे जाँचें कि hero animation ने Core Web Vitals को नुकसान नहीं पहुँचाया?
पेज को वैसे टेस्ट करें जैसे एक धीमा फ़ोन उसे देखता है, न कि जैसे आपका लैपटॉप।
- LCP एलिमेंट ढूँढें। Chrome DevTools के Performance panel में एक पेज लोड रिकॉर्ड करें और देखें कि कौन सा एलिमेंट LCP के रूप में मार्क है। यह आपका poster या headline होना चाहिए, कोई video frame या देर से आई इमेज नहीं।
- LCP के हिस्से जाँचें। web.dev LCP को time to first byte, resource load delay, resource load duration और element render delay में बाँटता है। बड़ा load delay मतलब poster देर से मिला; बड़ा load duration मतलब वह बहुत भारी है।
- Layout shifts देखें। उसी रिकॉर्डिंग में layout shift entries दिखती हैं। जब canvas या वीडियो poster की जगह ले, तब एक भी नहीं होनी चाहिए।
- लोड के दौरान टैप करें। CPU throttling चालू करके, hero शुरू होते समय मेन्यू खोलें। अगर मेन्यू अटकता है, तो runtime बहुत जल्दी शुरू हो रहा है।
- अपने ऑपरेटिंग सिस्टम में reduced motion चालू करें और reload करें: hero एक still इमेज होना चाहिए और runtime network panel में नहीं दिखना चाहिए।
- Field data पर नज़र रखें। Lab tests सिर्फ़ गाइड हैं; असली विज़िट्स का 75th percentile मायने रखता है, इसलिए अपनी real-user monitoring या Chrome UX Report में पहले और बाद की तुलना करें।
Core Web Vitals को नुकसान पहुँचाए बिना hero animation कैसे जोड़ें
- Poster एक्सपोर्ट करें. एनिमेशन के पहले या resting फ़्रेम को hero के डिस्प्ले साइज़ पर एक छोटी WebP या AVIF के रूप में सेव करें।
- Poster को LCP एलिमेंट बनाएँ. इसे HTML में width, height और fetchpriority="high" के साथ img के रूप में रखें। इसे कभी lazy-load न करें।
- बॉक्स रिज़र्व करें. Hero container को aspect-ratio दें और poster तथा canvas या वीडियो को absolute positioning से उसके अंदर एक के ऊपर एक रखें।
- Runtime देर से शुरू करें. Rive, Lottie या three.js runtime को तभी import करें जब hero दिख रहा हो और ब्राउज़र idle हो, फिर load callback में poster छिपा दें।
- मोशन कम करें सेटिंग का ध्यान रखें. अगर prefers-reduced-motion reduce है, तो poster ही रखें और न runtime लोड करें, न वीडियो शुरू करें।
- Offscreen pause करें और मापें. एनिमेशन viewport से बाहर जाए तो उसे pause करें, फिर throttling के साथ और field data में LCP, CLS और INP जाँचें।
अक्सर पूछे जाने वाले सवाल
क्या autoplay होने वाला hero video LCP को नुकसान पहुँचाता है?
पहुँचा सकता है। वीडियो के लिए LCP poster के load time या पहले फ़्रेम के presentation time में से जो पहले हो, उसका इस्तेमाल करता है। हल्की poster इमेज LCP को तेज़ रखती है, भले ही वीडियो फ़ाइल बड़ी हो। Poster के बिना ब्राउज़र को पहला फ़्रेम दिखाने लायक वीडियो fetch करना पड़ता है।
क्या Rive या three.js canvas को LCP एलिमेंट गिना जाता है?
नहीं। web.dev की LCP एलिमेंट types की लिस्ट में images, SVG के अंदर image elements, videos, CSS background images और text blocks हैं, canvas नहीं। Canvas hero पर LCP आम तौर पर आपकी headline या canvas के नीचे वाली poster इमेज होती है, इसलिए उन्हें optimize करें।
क्या मुझे hero के लिए Rive WebAssembly फ़ाइल preload करनी चाहिए?
सिर्फ़ तब जब एनिमेशन ही पेज का मुख्य मकसद हो। Preloading डाउनलोड जल्दी शुरू कर देती है, लेकिन आपकी LCP इमेज से होड़ करती है। अगर आप preload करते हैं, तो Rive के docs कहते हैं कि preload URL, setWasmUrl() को दिए गए URL से बिल्कुल मेल खाना चाहिए, वरना फ़ाइल दो बार डाउनलोड होगी।
स्रोत
- Largest Contentful Paint (LCP), web.dev
- Optimize Largest Contentful Paint, web.dev
- Optimize resource loading with the Fetch Priority API, web.dev
- Optimize Cumulative Layout Shift, web.dev
- Interaction to Next Paint (INP), web.dev
- Optimize Interaction to Next Paint, web.dev
- Intersection Observer API, MDN Web Docs
- Window: requestIdleCallback() method, MDN Web Docs
- Lazy loading video, web.dev
- Replace animated GIFs with video for faster page loads, web.dev
- prefers-reduced-motion, MDN Web Docs
- Web (JS) runtime: Rive parameters, Rive
- Canvas vs WebGL2, Rive
- Preloading WASM, Rive
- Web (JS) runtime: state machine playback, Rive
- WebGLRenderer.setAnimationLoop, three.js docs
- Understanding SC 2.2.2: Pause, Stop, Hide, W3C WAI
- Understanding SC 2.3.1: Three Flashes or Below Threshold, W3C WAI
संबंधित गाइड
- एनिमेशन फ़ाइल साइज़ बेंचमार्क: Rive बनाम GIF बनाम WebM बनाम MOV बनाम MP4 (असली डेटा)
- वेबसाइट हेडर में एनिमेटेड लोगो कैसे जोड़ें
- एक्सेसिबल एनिमेशन: Reduced Motion और WCAG
- वेबसाइट पर ट्रांसपेरेंट वीडियो (Safari सहित)
- Rive बनाम Lottie: परफ़ॉर्मेंस और रनटाइम साइज़
- स्क्रॉल से चलने वाला लोगो और मैस्कॉट एनिमेशन: CSS, Rive और वीडियो