Zapobieganie kumulatywnym zmianom układu z użyciem tła wideo w Next.js
Naucz się praktycznych technik CSS i układu, aby zapobiec temu, by filmy w tle w Next.js powodowały kumulatywne przesunięcia układu na różnych urządzeniach i przy różnych prędkościach łącza.
Istnieje określony typ błędu frontendu, który pozostaje niewidoczny podczas rozwoju w idealnych warunkach, ale gdy tylko ograniczysz prędkość łącza, dodane przez ciebie tło wideo sprawia, że strona zaczyna się drgać, zupełnie jak w chaotycznej scenie z sitcomu.
Gratulacje: właśnie spotkałeś się z zjawiskiem Cumulative Layout Shift.
Tła wideo łatwo umykają kontroli, ponieważ znajdują się na tle strony, zachowują się dobrze przy szybkim połączeniu, a inżynierowie zazwyczaj traktują je jako czysto wizualną warstwę, a nie element biorący udział w układzie strony.
Brauzer jednak nie podziela naszych założeń. Gdy rozmiar elementu jest nieznany w momencie pierwszego renderowania, brauzer musi zgadywać. A to właśnie te domysły nie powinny decydować o układzie strony.
Gdy już to wyjaśniliśmy, przejdźmy do szczegółów technicznych.
Zasada podstawowa: Zarezerwuj miejsce przed załadowaniem wideo
Cumulative Layout Shift mierzy, o ile widoczny kontent przesuwa się niespodziewanie. Najprostszym sposobem na uniknięcie tego jest przydzielenie elementom multimedialnym przewidywalnych, znanych wymiarów przed zakończeniem pobierania ich rzeczywistych zasobów. Ustawienie wyraźnej szerokości i wysokości lub użycie właściwości CSS aspect-ratio pozwala przeglądarce zarezerwować to miejsce z góry podczas obliczania układu.
Mówiąc prościej: zarezerwuj miejsce dla elementu, zanim faktycznie zostanie wyrenderowany.
Dla sekcji głównej z wideo w tle dobrym punktem wyjścia jest kontener o stałych wymiarach, zamiast pozwalać samej tagowi <video> decydować o rozmiarze tej sekcji.
export function VideoHero() {
return (
<section className="relative min-h-[70svh] overflow-hidden">
<video
className="absolute inset-0 h-full w-full object-cover"
autoPlay
muted
loop
playsInline
preload="metadata"
aria-hidden="true"
>
<source src="/hero-video.mp4" type="video/mp4" />
</video>
<div className="relative z-10 mx-auto max-w-6xl px-6 py-24">
<h1 className="text-5xl font-bold text-white">
Build products people remember.
</h1>
</div>
</section>
);
}
Kluczową częścią tutaj wcale nie jest zapis wideo.
Jest to ta jedna linia:
min-height: 70svh;
Z tego powodu sekcja z bohaterem ma już określony wygląd przed nawet rozpoczęciem odtwarzania filmu.
Sam film jest umieszczony absolutnie wewnątrz tego kontenera, co oznacza, że nie może nagle przesunąć zawartości poniżej siebie po zakończeniu ładowania zasobów.
To jedno rozróżnienie zmienia wszystko w kwestii stabilności strony.
Nie pozwól, by film określał układ strony
Typowa, problematyczna konfiguracja wygląda tak:
<video
src="/hero-video.mp4"
autoPlay
muted
loop
/>
Następnie ktoś dodaje:
video {
width: 100%;
}
A później zastanawia się, dlaczego strona zachowuje się niespójnie w zależności od jakości połączenia lub rozdzielczości ekranu.
Problem polega na tym, że przeglądarz musi znać wymiary filmu z góry. Jeśli film jest wyłącznie dekoracyjną treścią tła, rzadko istnieje powód, by brał udział w normalnym przepływie dokumentu.
Zamiast tego przekaż odpowiedzialność za układ tekstu kontenerowi otaczającemu, czyli div-owi, który znajduje się wokół elementu wideo.
<div style="height: 100px; width: 100%">
<video
src="/hero-video.mp4"
autoPlay
muted
loop
/>
</div>
Dzięki taka strukturze wideo może zachowywać się tak, jak chce wewnątrz, ale otaczający je div utrzymuje to wszystko w ramach.
Użyj object-fit: cover dla wideo tła
Gdy wideo zostanie umieszczone w pozycji absolutnej, object-fit: cover staje się naprawdę przydatne (to rzadki przypadek właściwości CSS, która działa dokładnie tak, jak zamierzono).
.heroVideo {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
To pozwala wideo wypełnić całą przeznaczoną obszar hero bez żadnej modyfikacji własnych wymiarów kontenera.
Należy zwrócić uwagę na niewielką kompromis. Wartość cover obcięje części wideo, gdy stosunek boków widoku nie będzie odpowiadał stosunkowi boków oryginalnego nagrania. To akceptowalna cena.
Prawdziwym błędem jest stawianie na idealne zachowanie oryginalnego wideo pod względem pikseli, podczas gdy projekt faktycznie wymaga stabilnej i przewidywalnej struktury.
Użyj plakatu jako pierwszego stanu wizualnego
Jedną z szczególnie skutecznych technik jest użycie dobrze dobranej grafiki plakatu dla elementu wideo.
<video
className="absolute inset-0 h-full w-full object-cover"
autoPlay
muted
loop
playsInline
preload="metadata"
poster="/images/hero-poster.webp"
aria-hidden="true"
>
<source src="/videos/hero.mp4" type="video/mp4" />
</video>
Plakat daje odwiedzającym coś konkretnego do oglądania, podczas gdy rzeczywiste wideo jest nadal pobierane. Wybierz tę grafikę starannie, ponieważ stanowi ona w praktyce twoją alternatywną wersję projektu.
Co ważniejsze, oznacza to, że twoja struktura nie będzie już zależeć od natychmiastowej dostępności zasobu wideo.
Traktuj plakat jako niezawodny stan bazowy, a samo wideo umieść na nim jako element ulepszenia stopniowego, podobnie jak działałby ładowacz szkieletu lub ikona spinera w innych kontekstach.
Jeśli wideo potrzebuje kilku sekund, aby dotrzeć przy wolnym połączeniu mobilnym, strona nadal wygląda na dobrze przygotowaną i kompletną, a nie uszkodzoną lub częściowo załadowaną.
Uwaga na pułapki związane z 100vh
Wideo w pełnym ekranie niesie ze sobą kolejną subtelną pułapkę.
Kiedyś programiści często pisali:
height: 100vh;
Przeglądarki mobilne komplikują tę sytuację, ponieważ wysokość widocznego obszaru roboczego zmienia się w zależności od pojawiania się i znikania elementów przeglądarki (paska adresu, pasków narzędzi), więc 100vh nie zachowuje się tak, jak można by oczekiwać.
Dla układów, które muszą dobrze dostosowywać się do różnych urządzeń, zazwyczaj lepiej jest używać nowszych jednostek obszaru roboczego, takich jak:
min-height: 100svh;
lub, w zależności od tego, jak dużą część ekranu chcemy, aby wypełniało wideo:
min-height: 80svh;
Krótko mówiąc: preferuj 100dvh lub 100svh zamiast starszego 100vh.
Unikaj wyświetlania filmów w rozdzielczości desktopowej użytkownikom mobilnym
Nawet strona o doskonałym wskaźniku CLS może wydawać się nieznośnie wolna, jeśli sam film w tle jest ogromny.
Tutaj praca nad wydajnością staje się bardziej złożona.
Bogaty, kinowy klip o wadze 12 MB może pięknie działać przy szybkim połączeniu desktopowym, ale na urządzeniu mobilnym przez niestabilną sieć ten sam plik staje się prawdziwym obciążeniem.
Dla większości stron startowych sensowne jest dostarczanie oddzielnych plików wideo dla ekranów desktopowych i mobilnych.
W niektórych przypadkach właściwym rozwiązaniem jest całkowite pominęcie filmu dla określonych odwiedzających.
Dobrym przykładem jest uwzględnienie preferencji użytkownika dotyczących ograniczenia ruchu na ekranie:
const prefersReducedMotion =
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
W rzeczywistym aplikacji React ta weryfikacja musi znajdować się w komponencie klienckim, a należy ją obsłużyć starannie, aby nie zakłóciła HTML wyświetlonego na serwerze.
Zasada jest prosta: decyzje dotyczące wydajności i dostępności powinny określać, czy wideo w ogóle ma być odtwarzane, a nie tylko jak szybko zostanie pobrałe.
Nigdy nie pozwól, by wideo dyktowało układ
To jest zasada przewodnia, którą należy mieć na uwadze przez cały czas.
Struktura Twojej strony powinna dobrze funkcjonować przy takiej sekwencji:
Hero container
↓
Poster
↓
Video enhancement
i nigdy nie polegaj na tej:
Video starts loading
↓
Browser discovers dimensions
↓
Hero changes height
↓
Everything below moves
↓
Lighthouse gets angry
Przeglądarka musi już znać Twój układ, zanim pojawi się cięższy plik multimedialny.
To jest rzeczywiste rozwiązanie problemów CLS spowodowanych wideo.
Aby to sprawdzić w praktyce, uruchom swoją stronę przy ograniczonej, wolniejszej łączności sieciowej i obserwuj jej zachowanie.
Powiązane artykuły
- Odroczenie skutków bocznych w Next.js za pomocą API after() — Dowiedz się, jak API after() w Next.js uruchamia analizy, logowanie oraz zadania w tle po wysłaniu odpowiedzi, a także jakie są jego gwarancje, pułapki i kompromisy w obszarze obsługi błędów.
- Dostosowywanie nowych mechanizmów dzielenia na chunki w Turbopack dla Next.js 16.3 — Praktyczny przegląd nowej konfiguracji turbopackChunking w Next.js 16.3, wyjaśniający, jak maxChunkCountPerGroup i generateComponentChunks wpływają na rozmiar pliku pakietu oraz funkcjonowanie cache’u.