Comment NRK donne vie à ses histoires grâce aux animations liées au défilement

Publié le 26 février 2026

Les animations basées sur le défilement sont passées d'implémentations JavaScript saccadées sur le thread principal à des expériences fluides, accessibles et hors thread principal à l'aide de fonctionnalités CSS et d'UI modernes telles que les chronologies de défilement et les chronologies de vue. Ce changement permet un prototypage rapide et des animations hautes performances, tout en permettant aux équipes de créer des pages scrollytelling soignées, comme illustré dans cet article.

NRK et le storytelling

NRK (Norwegian Broadcasting Corporation) est le diffuseur de service public en Norvège. L'équipe à l'origine de l'implémentation décrite dans cet article s'appelle Visuelle Historier en norvégien, ce qui se traduit par Visual Stories en anglais. L'équipe s'occupe de la conception, de la création graphique et du développement de projets éditoriaux pour la télévision, la radio et le Web. Elle développe des identités visuelles, des graphiques de contenu, des articles de fond et de nouveaux formats de storytelling visuel. L'équipe travaille également sur le profil de conception et les sous-marques de NRK, en créant des outils et des modèles pour faciliter la publication de contenus conformes à l'identité de la marque NRK.

Comment NRK utilise les animations liées au défilement

Les animations déclenchées par le défilement améliorent leurs articles de storytelling en les rendant plus interactifs, captivants et mémorables. Cette approche est particulièrement utile dans les récits de non-fiction où peu ou pas d'images sont disponibles.

Ces animations permettent de renforcer ou de créer des points dramaturgiques, de faire avancer l'histoire et de développer de petits récits visuels qui s'alignent sur le texte ou le renforcent. Basées sur le défilement, ces animations permettent à l'utilisateur de contrôler la progression du récit en faisant défiler l'écran.

Améliorer l'expérience utilisateur

Les insights utilisateur de NRK révèlent que les lecteurs apprécient la façon dont ces animations guident leur attention. En mettant en surbrillance du texte ou des animations pendant le défilement, les utilisateurs peuvent identifier plus facilement les points clés et comprendre les aspects les plus importants de l'histoire, en particulier lorsqu'ils effectuent un survol.

De plus, l'animation des graphiques peut simplifier les informations complexes, ce qui permet aux utilisateurs de comprendre plus facilement les relations et les changements au fil du temps. En créant, ajoutant ou mettant en avant des informations de manière dynamique, NRK peut présenter du contenu de manière plus pédagogique et attrayante.

Créer une ambiance

Les animations peuvent être des outils puissants pour définir ou améliorer l'ambiance d'une histoire. En ajustant le timing, la vitesse et le style des animations, NRK peut évoquer des émotions qui correspondent au ton du récit.

Structurer le texte et créer un contraste visuel

NRK utilise souvent de petites illustrations animées pour diviser de longs blocs de texte sous la forme d'un simple dinkus ou d'une petite illustration, ce qui permet aux lecteurs de faire une pause momentanée dans le récit. De nombreux utilisateurs apprécient cette variation, car elle permet de diviser le texte et de le rendre plus digeste. Ils estiment que cela offre une pause bienvenue dans le récit.

Respecter les besoins en matière d'accessibilité et les préférences des utilisateurs

Les pages publiques de la NRK doivent être accessibles à tous les citoyens norvégiens. Par conséquent, les pages doivent respecter la préférence de l'utilisateur pour un mouvement réduit. L'ensemble du contenu de la page doit être disponible pour les utilisateurs qui ont activé ce paramètre de navigateur.

Concevoir des animations liées au défilement

NRK a simplifié le workflow de conception en développant et en intégrant un nouvel outil d'animation de défilement directement dans son système de gestion de contenu (CMS) Sanity. Développé en collaboration entre les équipes qui développent et gèrent le site et les solutions de CMS, cet outil permet aux concepteurs de prototyper et d'implémenter facilement des animations de défilement avec des repères visuels pour les positions de début et de fin d'un élément animé, et de prévisualiser les animations en temps réel. Cette innovation offre aux concepteurs un plus grand contrôle et accélère le processus de conception directement dans le CMS.

Affichage de la zone affichée dans un outil après avoir fait défiler l'écran.
Exemple similaire de repères visuels pour les positions de début et de fin des éléments animés (et non l'outil CMS réel).

Animations liées au défilement dans le navigateur

Animation axée sur l'histoire

L'homme qui n'a pas été manqué

Cet article sur un homme qui est resté mort dans son appartement pendant neuf ans a dû s'appuyer fortement sur des illustrations en raison du manque d'autres éléments visuels. Les illustrations ont été animées par défilement pour souligner le récit. Par exemple, dans l'animation où la nuit tombe, les lumières d'un immeuble à plusieurs étages s'allument progressivement jusqu'à ce qu'un seul appartement reste éteint. L'animation a été créée à l'aide de l'outil d'animation interne de NRK basé sur le défilement.

Animation de fondu du texte

Pergélisol

Cet article commence par une brève introduction, comme la séquence d'ouverture d'un film. Des textes concis associés à des visuels en plein écran ont été conçus pour donner un aperçu du contenu de l'article, attiser l'intérêt des lecteurs et les inciter à lire l'article complet. La page de titre a été conçue pour ressembler à une affiche de film, avec des animations liées au défilement pour renforcer cette sensation en animant le texte de manière fluide vers le haut et vers l'extérieur.

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

Typographie animée lors du défilement

Typographie animée dans le titre d'un article : Congé maladie.

Avec l'introduction de "Sjukt sjuke" (qui se traduit approximativement par "Malade malade"), NRK a voulu attirer les lecteurs vers un article sur l'augmentation des congés maladie en Norvège. Le titre devait attirer l'attention des lecteurs et leur donner un indice sur le fait qu'il ne s'agit pas de l'histoire habituelle, ennuyeuse et axée sur les chiffres qu'ils pourraient s'attendre à lire. L'équipe de la NRK souhaitait que le texte et les illustrations jouent sur les thèmes de l'article, en utilisant la typographie et les animations de défilement pour renforcer cet effet. L'article utilise la nouvelle police et le nouveau profil de conception de 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"
    }
  );
}

Mettre en surbrillance les éléments mis à l'échelle

Enfants placés dans des institutions

Les lecteurs qui ont terminé un article souhaitent souvent en savoir plus sur le même sujet. Dans les articles sur les jeunes qui abusent de substances dans les établissements, NRK souhaitait recommander un seul article à lire ensuite, tout en offrant aux lecteurs la possibilité d'en consulter plusieurs autres s'ils le souhaitaient. La solution consistait en une navigation à faire glisser implémentée avec un accrochage de défilement et des animations basées sur le défilement. Les animations permettaient de mettre en surbrillance l'élément actif, tandis que les autres éléments étaient atténués.

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)";
}

Animation de défilement déclenchant une animation régulière

Budget :

Dans cet article sur le budget national de la Norvège, NRK a voulu rendre un sujet lourd et ennuyeux basé sur des chiffres plus accessible et personnalisé. L'objectif était de décomposer un chiffre budgétaire énorme et incompréhensible et de donner au lecteur un calcul personnel de ce à quoi sont dépensés ses impôts. Chaque sous-section était axée sur un poste spécifique du budget national. La contribution totale du lecteur aux impôts était symbolisée par une barre bleue divisée en sections pour indiquer sa contribution à chacun de ces éléments. La transition a été réalisée à l'aide d'une animation basée sur le défilement qui a déclenché l'animation des éléments individuels.

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"
    }
  );
}

"Nous utilisons les animations liées au défilement depuis longtemps. Avant l'existence de l'API Web Animations, nous devions utiliser des événements de défilement, combinés plus tard à l'API Intersection Observer. C'était souvent une tâche très chronophage, mais les API Web Animations et Scroll-Driven Animations la rendent désormais simple." —Helge Silset, développeur front-end chez NRK

NRK propose de nombreux composants Web différents qui peuvent être connectés à l'un de ses éléments personnalisés, appelé ScrollAnimationDriver (<scroll-animation-driver>), compatible avec les animations suivantes:

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

L'exemple suivant utilise des calques avec 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>

Implémentation JavaScript de l'élément personnalisé <scroll-animation-driver> de 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))
  }
}

Performances de défilement

NRK disposait d'une implémentation JavaScript très performante avant l'utilisation d'animations basées sur le défilement. Toutefois, les animations basées sur le défilement leur permettent désormais d'obtenir des performances encore meilleures sans avoir à se soucier des à-coups de défilement, même sur les appareils à faible consommation d'énergie.

  • Durée de la tâche non SDA: 1 ms.
  • Durée de la tâche SDA: 0,16 ms.
.
Onglet &quot;Performances&quot; des outils pour les développeurs Chrome
L'enregistrement dans l'onglet "Performances" des outils pour les développeurs Chrome avec un ralentissement du processeur de six fois indique 0,16 ms pour chaque tâche dans un nouveau frame.

Pour en savoir plus sur la différence de performances de défilement entre les implémentations JavaScript et les animations basées sur le défilement, consultez l'article Étude de cas sur les performances des animations basées sur le défilement.

Considérations d'accessibilité et d'expérience utilisateur

L'accessibilité joue un rôle important dans les pages publiques de la NRK, car elles doivent être accessibles à tous les citoyens norvégiens dans de nombreuses circonstances. NRK s'assure que les animations de défilement sont accessibles de plusieurs manières:

  • Respecter les préférences de l'utilisateur en matière de mouvement réduit: utilisez la requête multimédia screen and (prefers-reduced-motion: no-preference) pour appliquer l'animation en tant qu'amélioration progressive. Il est également utile de gérer les styles d'impression en même temps.
  • Compte tenu de la grande variété d'appareils et de la précision variable de la saisie de défilement: certains utilisateurs peuvent faire défiler l'écran par étapes (avec la touche Espace ou les touches Haut/Bas, en accédant à des repères à l'aide d'un lecteur d'écran) et ne pas voir l'animation entière. Assurez-vous que les informations essentielles ne sont pas manquées.
  • Soyez prudent avec les animations qui affichent ou masquent du contenu: pour les utilisateurs qui s'appuient sur le zoom du système d'exploitation (OS), il peut être difficile de remarquer que du contenu masqué apparaît à mesure qu'ils font défiler la page. Évitez de demander aux utilisateurs de le rechercher. Si vous devez masquer ou afficher du contenu, assurez-vous que son apparition et sa disparition sont cohérentes.
  • Éviter les variations importantes de luminosité ou de contraste dans l'animation: comme les animations basées sur le défilement dépendent du contrôle de l'utilisateur, les changements de luminance brusques peuvent apparaître comme des clignotements, ce qui peut déclencher des crises chez certains utilisateurs.
@media (prefers-reduced-motion: no-preference) {
  .article-image {
    opacity: 0;
    transition: opacity 1s ease-in-out;
  }
  .article-image.visible {
    opacity: 1;
  }
}

Prise en charge des navigateurs

Pour une compatibilité plus large des navigateurs avec ScrollTimeline et ViewTimeline, NRK utilise un polyfill Open Source, auquel une communauté active contribue.

Actuellement, le polyfill est chargé de manière conditionnelle lorsque ScrollTimeline n'est pas disponible et utilise une version allégée du polyfill sans prise en charge du CSS.

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

Détection et gestion de la compatibilité des navigateurs en 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%;
  }
}

Dans l'exemple précédent pour les navigateurs non compatibles, NRK utilise une variable CSS, --fallback-progress, comme solution de secours pour contrôler la timeline d'animation des propriétés translate et opacity.

La variable CSS --fallback-progress est ensuite mise à jour avec un écouteur d'événement scroll et requestAnimationFrame en JavaScript comme suit:

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;
  });
}

Ressources

Un merci tout particulier à Hannah Van Opstal, Bramus et Andrew Kean Guan de Google, ainsi qu'à Ingrid Reime de NRK pour leur précieuse contribution à ce travail.