Lazy loading implementacija vaizdams ir video

Kas ta lazy loading ir kodėl ji mums reikalinga

Jei kada nors kūrėte svetainę su daugybe vaizdų ar video įrašų, tikriausiai susidūrėte su problema – puslapis kraunasi lėtai kaip vėžlys. Vartotojai nervingai spaudžia F5, o jūsų Google PageSpeed Insights rezultatai primena mokyklinius pažymius po nesėkmingo kontrolinio. Čia ir ateina į pagalbą lazy loading – technika, kuri leidžia krauti turinį tik tada, kai jis tikrai reikalingas.

Esmė paprasta: kam krauti 50 vaizdų, jei vartotojas mato tik pirmuosius 3-4? Lazy loading atideda resursų krovimą, kol vartotojas neprislenka prie jų. Tai ne tik pagerina puslapio greičio rodiklius, bet ir sutaupo duomenų perdavimo kiekį, o tai ypač svarbu mobiliesiems vartotojams.

Pagalvokite apie tai kaip apie bufetą – jūs neužsikraunate lėkštės viskuo iš karto, o grįžtate po truputį. Taip ir naršyklė nekrauna visko iš karto, o tik tai, kas matoma ekrane (arba netrukus bus matoma).

Native lazy loading – paprasčiausias būdas

Gera žinia – šiuolaikinės naršyklės palaiko lazy loading natyviai, be jokių papildomų bibliotekų. Tai reiškia, kad dažniausiai jums pakanka pridėti vieną atributą prie <img> ar <iframe> tago:

<img src="didelis-vaizdas.jpg" loading="lazy" alt="Aprašymas">

Štai ir viskas! Naršyklė pati pasirūpins, kada krauti vaizdą. Chrome, Firefox, Safari ir Edge palaiko šį atributą jau kelerius metus. Pagal nutylėjimą naršyklė pradeda krauti vaizdą maždaug 1250-2500 pikselių prieš jam pasirodant ekrane – tai užtikrina, kad vartotojas nematys tuščių vietų.

Tas pats veikia ir su video:

<video controls loading="lazy">
<source src="video.mp4" type="video/mp4">
</video>

Tačiau yra vienas niuansas – loading="lazy" veikia tik su <video> tagu, o ne su <iframe> įterptais YouTube ar Vimeo video. Tiesa, <iframe> palaiko lazy loading, tad YouTube įterpimui galite naudoti:

<iframe src="https://www.youtube.com/embed/..." loading="lazy"></iframe>

Kada native lazy loading nepakanka

Native lazy loading puikus, bet turi apribojimų. Pirma, jūs negalite tiksliai kontroliuoti, kada prasideda krovimas. Antra, senesnės naršyklės (IE, senesni Safari) šito nežino. Trečia, kartais norite sudėtingesnės logikos – pavyzdžiui, krauti skirtingos raiškos vaizdus priklausomai nuo ekrano dydžio.

Čia praverčia JavaScript bibliotekos. Populiariausios – Intersection Observer API (ne biblioteka, bet naršyklės funkcija) ir ant jos pagrindo sukurtos bibliotekos kaip lazysizes, lozad.js ar vanilla-lazyload.

Intersection Observer leidžia stebėti, kada elementas pasirodo viewport’e. Štai paprastas pavyzdys:

const images = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove(‘lazy’);
observer.unobserve(img);
}
});
});

images.forEach(img => imageObserver.observe(img));

HTML atrodytų taip:

<img data-src="tikrasis-vaizdas.jpg" class="lazy" alt="Aprašymas">

Čia vietoj src naudojame data-src atributą. Kai vaizdas pasirodo ekrane, JavaScript perkelia URL iš data-src į src, ir vaizdas pradedamas krauti.

Responsive vaizdai ir lazy loading kombinacija

Dabar darosi įdomiau. Jei naudojate responsive vaizdus su <picture> elementu ar srcset atributu, lazy loading tampa šiek tiek sudėtingesnis, bet ir galingesnis.

Štai kaip atrodo responsive lazy loading su srcset:

<img
data-srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
data-sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
data-src="large.jpg"
class="lazy"
alt="Responsive vaizdas">

Kai JavaScript aktyvuoja krovimą, jis perkelia data-srcset į srcset, ir naršyklė pati pasirenka tinkamiausią vaizdą pagal ekrano dydį ir raiškos tankį.

Biblioteka lazysizes čia labai padeda – ji automatiškai tvarko visus šiuos atributus ir netgi skaičiuoja optimalius sizes atributus. Jums tereikia įtraukti biblioteką ir pridėti class="lazyload":

<img
data-srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
data-sizes="auto"
class="lazyload"
alt="Vaizdas">

Video lazy loading – ypatingas atvejis

Su video situacija sudėtingesnė. Video failai dažniausiai daug didesni už vaizdus, todėl lazy loading čia dar svarbesnis. Tačiau yra keletas strategijų.

Jei naudojate <video> tagą su autoplay, galite naudoti placeholder vaizdą ir krauti video tik kai reikia:

<video class="lazy" autoplay muted loop playsinline data-src="video.mp4" poster="placeholder.jpg"></video>

JavaScript:

const videos = document.querySelectorAll('video.lazy');

const videoObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const video = entry.target;
const source = document.createElement(‘source’);
source.src = video.dataset.src;
source.type = ‘video/mp4’;
video.appendChild(source);
video.load();
video.classList.remove(‘lazy’);
videoObserver.unobserve(video);
}
});
});

videos.forEach(video => videoObserver.observe(video));

Svarbu: jei video turi autoplay, nepamirškite muted atributo – naršyklės neleidžia automatiškai groti video su garsu be vartotojo sąveikos.

YouTube ir kiti įterptiniai video – atskira istorija. Čia galite naudoti facade pattern – rodyti placeholder vaizdą su play mygtuku, o tikrąjį iframe krauti tik paspaudus:

<div class="video-facade" data-video-id="dQw4w9WgXcQ">
<img src="youtube-thumbnail.jpg" alt="Video thumbnail">
<button class="play-button">▶</button>
</div>

JavaScript:

document.querySelectorAll('.video-facade').forEach(facade => {
facade.addEventListener('click', function() {
const videoId = this.dataset.videoId;
const iframe = document.createElement('iframe');
iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
iframe.allow = 'autoplay';
this.replaceWith(iframe);
});
});

Šis metodas sutaupo DAUG duomenų – YouTube iframe krauna apie 500KB-1MB resursų net nepradėjus groti video.

Placeholder strategijos ir UX

Viena didžiausių lazy loading problemų – content layout shift (CLS). Kai vaizdas užsikrauna, jis „išstumia” žemiau esantį turinį, ir puslapis šokinėja. Tai erzina vartotojus ir blogina SEO.

Sprendimas – rezervuoti vietą vaizdui prieš jam užsikraunant. Yra keletas būdų:

**1. Aspect ratio trikis su padding:**

.lazy-container {
position: relative;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
height: 0;
overflow: hidden;
}

.lazy-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

**2. Naujas CSS aspect-ratio:**

img.lazy {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}

**3. Blur-up technika (kaip Medium):**

Pirmiausia užkraunate mažą, labai suspaudą vaizdą (pvz., 20x15px), jį išdidinate ir pritaikote blur filtrą. Kai tikrasis vaizdas užsikrauna, blur išnyksta:

<div class="img-wrapper">
<img src="tiny-placeholder.jpg" class="img-placeholder" alt="">
<img data-src="full-image.jpg" class="lazy" alt="Aprašymas">
</div>

.img-wrapper {
position: relative;
}

.img-placeholder {
filter: blur(20px);
transform: scale(1.1);
}

.lazy {
position: absolute;
top: 0;
left: 0;
opacity: 0;
transition: opacity 0.3s;
}

.lazy.loaded {
opacity: 1;
}

Šis metodas sukuria įspūdį, kad vaizdas kraunasi palaipsniui, nors iš tikrųjų tiesiog keičiasi vienas vaizdas kitu.

Performance optimizacijos ir geriausios praktikos

Lazy loading pats savaime nėra stebuklingas sprendimas. Jei jį blogai implementuosite, galite net pabloginti situaciją. Štai keletas patarimų:

**Nekraukite lazy loading ant hero vaizdų.** Vaizdai, kurie matomi iš karto puslapį atidarius (above the fold), turėtų būti kraunami normaliai. Lazy loading čia tik pridės nereikalingą vėlavimą.

**Naudokite priority hints.** Naujesnės naršyklės palaiko fetchpriority atributą:

<img src="hero.jpg" fetchpriority="high" alt="Svarbus vaizdas">
<img src="footer-logo.jpg" loading="lazy" fetchpriority="low" alt="Logotipas">

**Optimizuokite pačius vaizdus.** Lazy loading nesumažins 5MB JPEG failo. Naudokite modernius formatus (WebP, AVIF), suspaudkite vaizdus, naudokite CDN.

**Testuokite su throttled connection.** Chrome DevTools leidžia simuliuoti lėtą internetą. Patikrinkite, kaip jūsų lazy loading veikia su 3G ar net 2G greičiu.

**Root margin adjustment.** Intersection Observer turi rootMargin parametrą, leidžiantį pradėti krauti vaizdus anksčiau:

const imageObserver = new IntersectionObserver((entries) => {
// ...
}, {
rootMargin: '50px 0px' // Pradeda krauti 50px prieš vaizdui pasirodant
});

Eksperimentuokite su šia verte – per maža, ir vartotojai matys tuščias vietas; per didelė, ir prarandate lazy loading privalumus.

**Fallback senoms naršyklėms.** Jei rūpinasi IE11 palaikymas (užuojauta), įtraukite <noscript> fallback:

<img data-src="image.jpg" class="lazy" alt="Aprašymas">
<noscript>
<img src="image.jpg" alt="Aprašymas">
</noscript>

Ką daryti su background images

CSS background images – dar vienas iššūkis. Native loading="lazy" čia neveikia, nes tai ne HTML elementas. Reikia JavaScript:

<div class="lazy-background" data-bg="background.jpg">
<!-- Turinys -->
</div>

const lazyBackgrounds = document.querySelectorAll('.lazy-background');

const bgObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.backgroundImage = `url(${entry.target.dataset.bg})`;
entry.target.classList.add(‘loaded’);
bgObserver.unobserve(entry.target);
}
});
});

lazyBackgrounds.forEach(bg => bgObserver.observe(bg));

Jei naudojate responsive background images su media queries, galite saugoti skirtingus URL’us data atributuose:

<div
class="lazy-background"
data-bg-mobile="bg-small.jpg"
data-bg-tablet="bg-medium.jpg"
data-bg-desktop="bg-large.jpg">
</div>

Ir JavaScript nustato tinkamą priklausomai nuo ekrano pločio.

Framework-specific implementacijos

Jei naudojate React, Vue ar kitą framework’ą, lazy loading tampa dar paprastesnis.

**React:** Naudokite react-lazy-load-image-component arba tiesiog native atributą:

function MyComponent() {
return (
<img
src="image.jpg"
loading="lazy"
alt="Aprašymas"
/>
);
}

Arba su biblioteka:

import { LazyLoadImage } from 'react-lazy-load-image-component';

function MyComponent() {
return (
<LazyLoadImage
src=”image.jpg”
placeholderSrc=”placeholder.jpg”
effect=”blur”
alt=”Aprašymas”
/>
);
}

**Next.js:** Turi built-in Image komponentą su automatišku lazy loading:

import Image from 'next/image';

function MyComponent() {
return (
<Image
src=”/image.jpg”
width={800}
height={600}
alt=”Aprašymas”
loading=”lazy” // Arba „eager” pirmiems vaizdams
/>
);
}

**Vue:** Naudokite v-lazy direktyvą su vue-lazyload:

<template>
<img v-lazy="imageUrl" alt="Aprašymas">
</template>

Arba tiesiog native atributą, kaip ir React.

Kai viskas susideda į vieną paveikslą

Lazy loading nėra sudėtinga technologija, bet ji reikalauja dėmesio detalėms. Naudokite native loading="lazy" kur įmanoma – tai paprasčiausia ir greičiausia. Kai reikia daugiau kontrolės ar palaikymo senesnėms naršyklėms, Intersection Observer API yra jūsų draugas.

Nepamirškite, kad lazy loading – tik viena optimizacijos dalis. Jūs vis tiek turite rūpintis vaizdu kokybe, formatu, CDN, caching. Bet kai visa tai sudėta kartu, rezultatai būna įspūdingi – puslapiai kraunasi greičiau, vartotojai laimingesni, o jūsų serveris nebeverkia nuo apkrovos.

Praktiškai kiekvienas projektas, turintis daugiau nei kelis vaizdus, turėtų naudoti lazy loading. Tai viena iš tų retų situacijų, kai nedidelis kodo kiekis duoda didelį rezultatą. Tad jei dar nenaudojate – metas pradėti. Jūsų vartotojai (ir jų duomenų planai) bus dėkingi.

Parašykite komentarą

El. pašto adresas nebus skelbiamas. Būtini laukeliai pažymėti *