Animirani sajtovi imaju reputaciju sporih, i često je zaslužuju. Ali pokret i dobri Core Web Vitals nisu suprotnosti. Većina problema dolazi sa kratkog spiska uzroka, a za svaki postoji rešenje koje zadržava animaciju.
Ovaj sajt ima video u zaglavlju, WebGL zid sa projektima, WebGL tipografiju i animacije pri skrolovanju svuda. Ovo su pravila na kojima je napravljen.
Tri metrike, ukratko
| Metrika | Šta meri | Dobro |
|---|---|---|
| LCP — Largest Contentful Paint | Kada se pojavi glavni sadržaj | ≤ 2,5 s |
| INP — Interaction to Next Paint | Koliko brzo stranica reaguje na dodire i klikove | ≤ 200 ms |
| CLS — Cumulative Layout Shift | Koliko raspored poskakuje | ≤ 0,1 |
Google ih ocenjuje na 75. percentilu stvarnih Chrome korisnika (terenski podaci u PageSpeed Insights i Search Console), a ne na osnovu Lighthouse testa na vašem laptopu. Testirajte na Android telefonu srednje klase uz usporenu vezu; to je bliže vašem 75. percentilu od MacBooka.
LCP: pokažite nešto stvarno, rano
Ne skrivajte naslovnu sekciju iza ulazne animacije
Najčešći ubica LCP-a na animiranim sajtovima je naslovna sekcija koja počinje sa opacity: 0 i pojavljuje se tek kada se učita JavaScript. Pregledač ne može da je računa kao iscrtanu dok nije vidljiva, pa LCP čeka vaš bundle, a zatim i animaciju.
Rešenje: renderujte konačno stanje naslovne sekcije u HTML-u i CSS-u. Animirajte sporedne elemente, ili animirajte iz stanja koje je već vidljivo (blago skaliranje ili pomeranje, ne iz nulte prozirnosti).
Dajte LCP elementu poster i prioritet
Ako je naslovna sekcija video ili WebGL platno, LCP kandidat je obično njegova poster slika. Neka bude mala, unapred učitana i visokog prioriteta:
<link rel="preload" as="image" href="/hero/poster.avif" fetchpriority="high" />
<img src="/hero/poster.avif" alt="" fetchpriority="high" width="1600" height="900" />Video ili WebGL scenu pokrenite nakon što se poster iscrta, i pređite na nju postepeno.
Učitavajte teške biblioteke kada su potrebne
Three.js, GSAP pluginovi i Lottie ne moraju da budu u prvom bundle-u ako pokreću sekcije ispod prvog ekrana. Uvezite ih dinamički kada se sekcija približi vidljivom delu:
const observer = new IntersectionObserver(async ([entry]) => {
if (!entry.isIntersecting) return
observer.disconnect()
const { initScene } = await import('./scene')
initScene(canvas)
}, { rootMargin: '400px 0px' })
observer.observe(section)U Next.js, next/dynamic sa ssr: false radi isto za cele komponente.
Poslužujte medije odgovarajuće veličine
Video od 1440p na telefonu je bačen protok koji se takmiči sa vašom LCP slikom. Birajte izvore prema širini ekrana i vezi, a video petlje u zaglavlju neka budu kratke (6–10 sekundi, bez audio zapisa).
INP: oslobodite glavnu nit
INP meri kašnjenje između interakcije i sledećeg kadra. Animacija mu šteti kada je JavaScript zauzet u svakom kadru.
Animirajte samo transform i opacity
Njima upravlja compositor i ne izazivaju ponovni raspored. Animiranje top, left, width, height ili margin primorava raspored u svakom kadru:
/* Slow: layout every frame */
.card { transition: top 0.3s; }
/* Fast: compositor only */
.card { transition: transform 0.3s; }Ne čitajte raspored unutar scroll handlera
getBoundingClientRect() posle promene stila primorava sinhroni raspored. U scroll handlerima čitajte jednom po kadru unutar requestAnimationFrame, a zatim pišite:
let ticking = false
window.addEventListener('scroll', () => {
if (ticking) return
ticking = true
requestAnimationFrame(() => {
const top = section.getBoundingClientRect().top // read
progressBar.style.transform = `scaleX(${clamp(-top / range)})` // write
ticking = false
})
}, { passive: true })Još bolje, prepustite to biblioteci kao što je GSAP-ov ScrollTrigger, ili koristite CSS animacije vođene skrolovanjem (animation-timeline: scroll()) gde su podržane.
Zaustavite posao koji se ne vidi
Svaka requestAnimationFrame petlja na stranici radi na glavnoj niti. Pauzirajte platna, video zapise i petlje kada izađu iz vidljivog dela i kada je kartica skrivena:
const io = new IntersectionObserver(([e]) => (e.isIntersecting ? start() : stop()))
io.observe(canvas)
document.addEventListener('visibilitychange', () => (document.hidden ? stop() : start()))Ograničite rezoluciju platna
WebGL platno sa devicePixelRatio 3 na telefonu iscrtava devet puta više piksela nego pri DPR 1. Ograničite ga:
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))Za efekte preko celog ekrana, poput magle ili šuma, renderovanje u pola rezolucije pa uvećavanje često se ne razlikuje od originala.
Razbijte duge zadatke
Pravljenje scene, parsiranje velikog JSON-a ili pripremanje mnogo tekstualnih mreža može blokirati nit stotinama milisekundi. Podelite posao i prepustite nit između delova (await scheduler.yield() gde je dostupno, inače setTimeout(0)) kako bi dodir tokom pripreme dobio brz odgovor.
CLS: rezervišite prostor za sve
Dajte medijima dimenzije
Svaka slika, video, iframe i platno treba da ima atribute width/height ili CSS aspect-ratio, kako bi pregledač rezervisao prostor pre učitavanja.
Učitavajte fontove bez skoka
Dekorativni fontovi su deo karaktera animiranog sajta, a kasna zamena fonta pomera svaki red. Unapred učitajte jedan ili dva ključna fajla, smanjite ih na potrebne karaktere i koristite font-display: swap sa rezervnim fontom podešenih metrika (size-adjust, ascent-override) — next/font to generiše automatski.
Animacije se ne računaju — promene rasporeda da
CLS ignoriše transform animacije, ali ne i promene veličine ili položaja preko svojstava rasporeda. Baner koji posle učitavanja gura sadržaj nadole se računa; isti baner koji uklizava sa transform preko rezervisanog prostora — ne.
Poštujte smanjen pokret
Neki posetioci od operativnog sistema traže manje pokreta. Poštujte to — to je zahtev pristupačnosti, a ti posetioci dobijaju i lakšu stranicu:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}U JavaScriptu, proverite matchMedia('(prefers-reduced-motion: reduce)') pre pokretanja petlji, paralakse i video zapisa koji se sami puštaju.
Postupak koji hvata nazadovanja
- Snimite Performance trag u Chrome DevTools uz 4× usporen CPU dok skrolujete celu stranicu. Tražite duge zadatke i ljubičaste trake rasporeda.
- Uključite Rendering → Paint flashing i Layout Shift Regions da vidite šta se ponovo iscrtava i pomera.
- Posle lansiranja, nedeljno proveravajte terenske podatke u PageSpeed Insights; laboratorijski rezultati su smernica, terenski podaci su ocena.
Kontrolna lista
- Sadržaj naslovne sekcije vidljiv u početnom HTML-u, a ne iz nulte prozirnosti
- LCP slika unapred učitana sa
fetchpriority="high" - Teške biblioteke učitane po potrebi
- Animiraju se samo
transformiopacity - Čitanje rasporeda grupisano u
requestAnimationFrame - Petlje, platna i video van ekrana pauzirani
- Ograničen odnos piksela platna
- Dimenzije na svim medijima; fontovi sa rezervnim fontovima usklađenih metrika
- Poštuje se
prefers-reduced-motion
Za stranu animacija, pogledajte GSAP u React-u i Next.js-u.

