Jak NRK wykorzystuje animacje zależne od przewijania do ożywiania treści

Data publikacji: 26 lutego 2026 r.

Animacje sterowane przewijaniem przeszły od niewygodnych implementacji JavaScriptu na głównym wątku do płynnych, dostępnych i niezależnych od głównego wątku rozwiązań, które wykorzystują nowoczesne funkcje CSS i UI, takie jak osi czasu przewijania i osi czasu widoku. Ta zmiana umożliwia szybkie tworzenie prototypów i wydajnych animacji, a także pozwala zespołom tworzyć dopracowane strony opowiadania za pomocą przewijania, jak pokazano w tym artykule.

NRK i opowiadanie historii

NRK (Norweski Urząd Nadawczy) to nadawca publiczny w Norwegii. Zespół, który opisał implementację opisaną w tym artykule, nazywa się Visuelle Historier w języku norweskim, co w przybliżeniu oznacza Visual Stories w języku angielskim. Zespół zajmuje się projektowaniem, grafiką i rozwojem w ramach projektów redakcyjnych dla telewizji, radia i sieci, opracowując tożsamości wizualne, grafiki treści, artykuły tematyczne oraz nowe formaty wizualnego opowiadania historii. Zespół współpracuje też z NRK w zakresie profilu projektowego i podmarek, tworząc narzędzia i szablony, które ułatwiają publikowanie treści zgodnych z tożsamością marki NRK.

Jak NRK używa animacji sterowanych przez przewijanie

Animacje uruchamiane przez przewijanie i w reakcji na przewijanie wzbogacają artykuły o elementy interaktywne, zwiększają ich atrakcyjność i ułatwiają zapamiętanie. Jest to szczególnie przydatne w przypadku treści niefikcjonalnych, w których przypadku dostępne są tylko nieliczne obrazy lub w ogóle ich brak.

Te animacje pomagają wzmacniać lub tworzyć punkty dramaturgiczne, rozwijać fabułę i tworzyć małe narracje wizualne, które są zgodne z tekstem lub go wzmacniają. Te animacje są sterowane przez przewijanie, dzięki czemu użytkownik może kontrolować tempo narracji.

Ulepszenie wrażeń użytkowników

Statystyki użytkowników NRK wskazują, że czytelnicy doceniają to, jak te animacje pomagają im się skupić. Dzięki podświetlaniu tekstu lub animacji podczas przewijania użytkownicy łatwiej mogą dostrzec najważniejsze punkty i zrozumieć najważniejsze aspekty historii, zwłaszcza gdy tylko ją przeglądają.

Dodatkowo animacja grafik może uprościć skomplikowane informacje, dzięki czemu użytkownicy łatwiej zrozumieją zależności i zmiany w czasie. Dzięki dynamiczowaniu tworzenia, dodawania i wyróżniania informacji NRK może prezentować treści w sposób bardziej przystępny i angażujący.

Ustawianie nastroju

Animacje mogą być potężnym narzędziem do tworzenia lub wzmacniania nastroju w historii. Dzięki dostosowywaniu czasu, szybkości i stylu animacji NRK może wywoływać emocje, które pasują do nastroju narracji.

Dzielenie tekstu i zapewnianie wizualnego odpoczynku

NRK często stosuje małe animowane ilustracje, aby podzielić długie bloki tekstu w postaci prostego dinkusa lub małej ilustracji, co daje czytelnikom chwilową przerwę od narracji. Wielu użytkowników ceni tę odmianę, ponieważ dzieli tekst na części i ułatwia jego czytanie. Uważają, że stanowi ona miłą przerwę w narracji.

Uwzględnianie potrzeb związanych z ułatwieniami dostępu i preferencji użytkowników

Publiczne strony NRK muszą być dostępne dla wszystkich obywateli Norwegii. Dlatego strony muszą uwzględniać preferencje użytkownika dotyczące ograniczonego ruchu. Cała treść strony musi być dostępna dla użytkowników, którzy włączyli to ustawienie przeglądarki.

Projektowanie animacji zależnych od przewijania

NRK usprawnił proces projektowania, opracowując i integrując nowe narzędzie do animacji przewijania bezpośrednio w systemie zarządzania treścią (CMS) Sanity. To narzędzie zostało opracowane we współpracy z zespołami zajmującymi się tworzeniem i utrzymywaniem witryny oraz rozwiązaniami CMS. Umożliwia ono projektantom łatwe tworzenie prototypów i wdrażanie animacji przewijania z wizualnymi wskazówkami dotyczącymi pozycji początkowej i końcowej animowanego elementu oraz umożliwia podgląd animacji w czasie rzeczywistym. Ta innowacja daje projektantom większą kontrolę i przyspiesza proces projektowania bezpośrednio w systemie CMS.

Wyświetlanie obszaru, który został przewinięty w narzędziu.
Podobny przykład podpowiedzi wizualnych dla pozycji początkowej i końcowej animowanych elementów (nie jest to rzeczywiste narzędzie CMS).

Animacje wywoływane przez przewijanie w przeglądarce

Animacja z fabułą

Człowiek, którego nie było widać

W tym artykule o człowieku, który przez 9 lat był martwy w swoim mieszkaniu, musieliśmy polegać głównie na ilustracjach ze względu na brak innych elementów wizualnych. Ilustracje zostały animowane podczas przewijania, aby podkreślić narrację. Przykładem jest animacja, w której z nadchodzącym zmrokiem światła w wielokondygnacyjnym budynku zapalają się stopniowo, aż w końcu tylko jedno mieszkanie pozostaje nieoświetlone. Animacja została stworzona za pomocą wewnętrznego narzędzia do tworzenia animacji opartej na przewijaniu.

Animacja znikania tekstu

Stała zmarzlina.

Ten artykuł rozpoczyna się krótkim wprowadzeniem, które naśladuje sekwencję otwierającą film. Krótkie teksty połączone z wizualizacjami na pełnym ekranie miały zachęcać do przeczytania całego artykułu. Strona tytułowa została zaprojektowana tak, aby przypominać plakat filmowy. Animacje uruchamiane podczas przewijania mają wzmacniać to wrażenie, płynnie przesuwając tekst w górę i w dół.

.article-section {
  animation: fade-up linear;
  animation-timeline: view();
  animation-range: entry 100% exit 100%;
}

Typografia z animacją podczas przewijania

Animowana typografia w tytule artykułu – Urlop na czas choroby.

Wprowadzenie w filmie „Sjukt sjuke” (co można przetłumaczyć jako „Bardzo chory”) miało zachęcić widzów do obejrzenia materiału o zwiększającej się liczbie dni absencji chorobowej w Norwegii. Tytuł miał przykuwać uwagę czytelników i wskazywać, że nie jest to zwykły, nudny tekst z liczbami. Zespół NRK chciał, aby tekst i ilustracje odzwierciedlały tematy utworu, dlatego wykorzystał typografię i animacje uruchamiane przez przewijanie. W artykule wykorzystano nową czcionkę i profil projektowy NRK News.

<h1 aria-label="sjuke">
  <span>s</span><span>j</span><span>u</span><span>k</span><span>e</span>
<h1>
h1 span {
  display: inline-block;
}
if (window.matchMedia('print, (prefers-reduced-motion: reduce)').matches) {
  return;
}

const heading = document.querySelector("h1");
const letters = heading.querySelectorAll("span");

const timeline = new ViewTimeline({ subject: heading });
const scales = [/**/];
const rotations = [/**/];

for ([index, el] of letters.entries()) {
  el.animate(
    {
      scale: ["1", scales[index]],
      rotate: ["0deg", rotations[index]]
    },
    {
      timeline,
      fill: "both",
      rangeStart: "contain 30%",
      rangeEnd: "contain 70%",
      easing: "ease-out"
    }
  );
}

Podświetlanie elementów przyciąganych przez przewijanie

Dzieci w instytucjach.

Czytelnicy, którzy skończyli czytać artykuł, często chcą dowiedzieć się więcej o tym samym zagadnieniu. W artykułach dotyczących młodzieży nadużywającej substancji w instytucjach NRK chciała polecić jeden artykuł do przeczytania, ale jednocześnie dać czytelnikom możliwość wyboru kilku innych. Rozwiązaniem było przesuwanie elementów nawigacji z wykorzystaniem animacji płynnego przewijania i animacji zależnych od przewijania. Animacje zapewniały, że aktywny element był w centrum uwagi, a pozostałe elementy były przyciemnione.

for (let item of items) {
  const timeline = new ViewTimeline({ subject: item, axis: "inline" });
  const animation = new Animation(effect, timeline);
  item.animate(
    {
      opacity: [0.3, 1, 0.3]
    },
    { timeline, easing: "ease-in-out", fill: "both" }
  );
  animation.rangeStart = "cover calc(50% - 100px)";
  animation.rangeEnd = "cover calc(50% + 100px)";
}

Animacja wywołana przez przewijanie

Budżet.

W tym artykule o budżecie narodowym Norwegii NRK chciało sprawić, aby ciężka i nudna historia oparta na liczbach była bardziej przystępna i spersonalizowana. Celem było rozłożenie na czynniki ogromnej i niezrozumiałej kwoty budżetowej oraz przedstawienie czytelnikom, na co idą ich podatki. Każdy z tych podrozdziałów skupia się na konkretnym elemencie budżetu państwa. Łączny podatek czytelnika został oznaczony niebieskim paskiem podzielonym na części, aby pokazać udział czytelnika w poszczególnych pozycjach. Przejście zostało uzyskane dzięki animacji sterowanej przez przewijanie, która wywołała animację poszczególnych elementów.

const timeline = new ViewTimeline({
  subject: containerElement
});

// Setup scroll-driven animation
const scrollAnimation = containerElement.animate(
  {
    "--cover-color": ["blue", "lightblue"],
    scale: ["1 0.2", "1 3"]
  },
  {
    timeline,
    easing: "cubic-bezier(1, 0, 0, 0)",
    rangeStart: "cover 0%",
    rangeEnd: "cover 50%"
  }
);

// Wait for scroll-driven animation to complete
await scrollAnimation.finished;
scrollAnimation.cancel();

// Trigger time-driven animations
for (let [index, postElement] of postElements.entries()) {
  const animation = postElement?.animate(
    { scale: ["1 3", "1 1"] },
    {
      duration: 200,
      delay: index * 33,
      easing: "ease-out",
      fill: "backwards"
    }
  );
}

„Od dawna tworzymy animacje uruchamiane przez przewijanie. Zanim pojawiło się Web Animations API, musieliśmy używać zdarzeń przewijania, a później połączyć je z interfejsem Intersection Observer API. To zadanie często zajmowało dużo czasu, ale dzięki interfejsom API animacji internetowych i animacji sterowanych przewijaniem jest teraz banalnie proste” – Helge Silset, programista front-endu w NRK

NRK ma wiele różnych komponentów internetowych, które można wstawić do jednego z elementów niestandardowych o nazwie ScrollAnimationDriver (<scroll-animation-driver>) obsługujących te animacje:

  • Warstwy z [KeyframeEffects](https://developer.mozilla.org/docs/Web/API/KeyframeEffect)
  • Animacje Lottie
  • mp4
  • three.js
  • <canvas>

W tym przykładzie użyto warstw z wartością KeyframeEffects:

<scroll-animation-driver data-range-start='entry-crossing 50%' data-range-end='exit-crossing 50%'>
  <layered-animation-effect>
    <picture>
      <source />
      <img />
    </picture>

    <picture>
      <source />
      <img />
    </picture>

    <picture>
      <source />
      <img />
    </picture>
  </layered-animation-effect>
</scroll-animation-driver>

Implementacja JavaScript elementu niestandardowego <scroll-animation-driver> firmy NRK:

export default class ScrollAnimationDriver extends HTMLElement {
  #timeline

  connectedCallback() {
    this.#timeline = new ViewTimeline({subject: this})
    for (const child of this.children) {
      for (const effect of child.effects ?? []) {
        this.#setupAnimationEffect(effect)
      }
    }
  }

  #setupAnimationEffect(effect) {
    const animation = new Animation(effect, this.#timeline) 
    animation.rangeStart = this.rangeStart
    animation.rangeEnd = this.rangeEnd

    if (this.prefersReducedMotion) {
      animation.currentTime = CSS.percent(this.defaultProgress * 100)
    } else {
      animation.play()
    }
  }
}

export default class LayeredAnimationEffect extends HTMLElement {
  get effects() {
    return this.layers.flatMap(layer => toKeyframeEffects(layer))
  }
}

Wydajność przewijania

Przed wprowadzeniem animacji opartych na przewijaniu NRK miała bardzo wydajną implementację JavaScriptu, ale teraz animacje oparte na przewijaniu pozwalają uzyskać jeszcze większą wydajność bez konieczności martwienia się o płynność przewijania nawet na urządzeniach o małej mocy.

  • Czas trwania zadania niebędącego zadaniem SDA: 1 ms.
  • Czas trwania zadania SDA: 0,16 ms.
.
Karta Wydajność w Narzędziach deweloperskich w Chrome
Nagranie na karcie Wydajność w Narzędziach deweloperskich w Chrome z 6-krotnym spowolnieniem procesora pokazuje 0,16 ms dla każdego zadania w nowym klatce.

Więcej informacji o różnicach w wydajności przewijania między implementacjami JavaScript a animacjami opartymi na przewijaniu znajdziesz w artykule Przypadek wykorzystania animacji opartych na przewijaniu.

Ułatwienia dostępu i wygody użytkownika

Dostępność odgrywa ważną rolę w przypadku stron publicznych NRK, ponieważ w wielu przypadkach muszą być one dostępne dla wszystkich obywateli Norwegii. NRK zapewnia, że animacje przewijania są dostępne na kilka różnych sposobów:

  • Uwzględnianie preferencji użytkowników dotyczących ograniczonego ruchu: użyj zapytania o multimediascreen and (prefers-reduced-motion: no-preference), aby zastosować animację jako ulepszenie progresywne. Przydaje się też, gdy jednocześnie chcesz obsługiwać style drukowania.
  • Uwzględnienie szerokiej gamy urządzeń i różnej precyzji sterowania za pomocą kółka przewijania: niektórzy użytkownicy mogą przewijać w krokach (spacja lub klawisze w górę/w dół, nawigacja do punktów orientacyjnych za pomocą czytnika ekranu) i nie widzieć całej animacji. Upewnij się, że nie pominięto żadnych ważnych informacji.
  • Uważaj na animacje, które wyświetlają lub ukrywają treści: użytkownicy korzystający z powiększenia w systemie operacyjnym mogą nie zauważyć, że ukryte treści pojawią się podczas przewijania. Nie zmuszaj użytkowników do szukania. Jeśli konieczne jest ukrycie lub wyświetlenie treści, zadbaj o to, aby pojawiały się i znikały w konsekwentny sposób.
  • Unikaj dużych zmian jasności lub kontrastu w animacji: animacje wywoływane przez przewijanie zależą od działań użytkownika, więc nagłe zmiany jasności mogą wyglądać jak miganie, co może wywołać u niektórych użytkowników napady drgawek.
@media (prefers-reduced-motion: no-preference) {
  .article-image {
    opacity: 0;
    transition: opacity 1s ease-in-out;
  }
  .article-image.visible {
    opacity: 1;
  }
}

Obsługa przeglądarek

Aby zapewnić szersze wsparcie przeglądarek w przypadku komponentów ScrollTimelineViewTimeline, NRK używa polyfilla typu open source, do którego aktywnie się przyczynia społeczność.

Obecnie polyfill jest ładowany warunkowo, gdy ScrollTimeline jest niedostępny, i korzysta z obciężonej wersji polyfilla bez obsługi CSS.

if (!('ScrollTimeline' in window)) {
  await import('scroll-timeline.js')
}

Wykrywanie i obsługa obsługi przeglądarek w CSS:

@supports not (animation-timeline: view()) {
  .article-section {
    translate: 0 calc(-15vh * var(--fallback-progress));
    opacity: var(--fallback-progress);
  }
}

@supports (animation-timeline: view()) {
  .article-section {
    animation: --fade-up linear;
    animation-timeline: view();
    animation-range: entry 100% exit 100%;
  }
}

W poprzednim przykładzie dotyczącym nieobsługiwanych przeglądarek NRK używa zmiennej CSS, --fallback-progress, jako zastępczego sposobu sterowania osią czasu animacji właściwości translateopacity.

Zmienna CSS --fallback-progress jest następnie aktualizowana za pomocą scroll listenera zdarzeń i requestAnimationFrame w JavaScript w ten sposób:

function updateProgress() {
  const end = el.offsetTop + el.offsetHeight;
  const start = end - window.innerHeight;
  const scrollTop = document.scrollingElement.scrollTop;
  const progress = (scrollTop - start) / (end - start);
  document.body.style.setProperty('--fallback-progress', clamp(progress, 0, 1));
}


if (!CSS.supports("animation-timeline: view()")) {
  document.addEventListener('scroll', () => {
    if (!visible || updating) {
      return;
    }

    window.requestAnimationFrame(() => {
      updateProgress();
      updating = false;
    });

    updating = true;
  });
}

Zasoby

Szczególne podziękowania dla Hannah Van Opstal, Bramusa, Andrew Keana Guana z Google oraz Ingrid Reime z NRK za ich cenny wkład w realizację tego projektu.