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

Data publikacji: 26 lutego 2025 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 wysokiej jakości animacji, a także pozwala zespołom tworzyć dopracowane strony oparte na przewijaniu, 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ć krótkie wizualne narracje, 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 skupić uwagę. Dzięki podświetlaniu tekstu lub animacji podczas przewijania użytkownicy łatwiej znajdą najważniejsze punkty i zrozumieją 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 relacji. Dostosowując czas, szybkość i styl 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ę w 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 systemów 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ł wyświetlony po przewinięciu 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 za zadanie zasugerować treść artykułu, wzbudzając ciekawość czytelników i zachęcając ich do zapoznania się z całym tekstem. 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 „Sjukt sjuke” (co można przetłumaczyć jako „Bardzo chory”) miało zachęcić czytelników do przeczytania artykułu o wzrostach liczby 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

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żiewają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 skanowania i 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łączyliśmy 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 podłączyć 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> w 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 niskiej 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 elementó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 używa uproszczonej 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 przypadku 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.

NRK zastrzega sobie wszelkie prawa do nazwy firmy, logo oraz artykułów, do których prowadzą linki lub z których pochodzą cytaty.