Créer un indicateur de progression de défilement

Un indicateur de progression du défilement est un modèle d'interface utilisateur courant qui communique visuellement la progression de l'utilisateur dans un document ou un conteneur à défilement. À mesure que l'utilisateur fait défiler la page, un élément visuel s'actualise pour refléter sa position, ce qui lui permet de savoir clairement et intuitivement quelle quantité de contenu a été consultée et combien il en reste.

Mise en œuvre

Pour créer un indicateur de progression du défilement, vous avez besoin de deux éléments :

  1. Un élément qui servira de barre de progression. Cet élément est généralement position: fixed ou position: absolute afin qu'il reste visible lorsque l'utilisateur fait défiler la page.
  2. Animation associée à la position de défilement.

Voici comment procéder :

  • Commencez par créer un élément HTML qui servira de barre de progression. Vous pouvez personnaliser le style de cet élément selon vos préférences.
  • Ensuite, dans votre CSS, définissez une animation @keyframes qui met à l'échelle la barre de progression. Une approche courante consiste à mettre à l'échelle l'élément de scaleX(0) à scaleX(1).
  • Enfin, appliquez cette animation à l'élément de la barre de progression et définissez sa propriété animation-timeline sur une timeline de défilement. Cela indique au navigateur de piloter la progression de l'animation en fonction de la position de défilement du conteneur de défilement ancêtre le plus proche.

Exemple de code

Ce code agrandit l'élément #progress lors du défilement à l'aide d'une chronologie de défilement anonyme, créée par la fonction scroll().

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: scroll();
    }
  }
}

En raison de son emplacement dans le DOM, la fonction scroll() suivra le défilement de son ancêtre le plus proche dans la direction block, qui est ici le défilement racine.

<body>
  <!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
  <div id="progress" aria-hidden="true"></div>
</body>

Ce code agrandit l'élément #progress lors du défilement à l'aide d'une timeline de défilement nommée, créée par la propriété scroll-timeline.

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    :root {
      scroll-timeline: --tl block;
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: --tl;
    }
  }
}

Bonnes pratiques

Lorsque vous utilisez des animations basées sur le défilement, il est important de suivre quelques bonnes pratiques pour garantir une expérience fluide et accessible :

  • À FAIRE : incluez la détection de fonctionnalités. Tous les navigateurs ne sont pas compatibles avec les animations déclenchées par le défilement. Utilisez @supports (animation-timeline: scroll()) pour vérifier la compatibilité et fournir une solution de remplacement pour les navigateurs qui ne sont pas compatibles.
    • N'UTILISEZ PAS le package scroll-timeline-polyfill pour la stratégie de remplacement, car il n'est pas complet et présente de nombreux problèmes connus.
    • Si l'animation n'est considérée que comme décorative, optez pour l'amélioration progressive et NE FOURNISSEZ PAS de solution de remplacement.
  • À FAIRE : Supprimez les éléments purement décoratifs des flux de lecture des technologies d'assistance. Appliquez aria-hidden="true" aux indicateurs de défilement purement visuels pour vous assurer que les lecteurs d'écran ne rencontrent pas de nœuds vides sans nom.
  • À FAIRE : Respectez les préférences des utilisateurs. Certains préfèrent avoir moins de mouvements sur le Web. Utilisez la requête média prefers-reduced-motion pour désactiver ou réduire vos animations pour ces utilisateurs.
  • À FAIRE : Essayez de n'animer que les propriétés CSS performantes. Pour des animations plus fluides, n'animez que les propriétés pouvant être gérées par le thread du compositeur du navigateur, telles que transform et opacity. L'animation d'autres propriétés telles que width ou height peut entraîner des problèmes de performances.
  • À FAIRE : Utilisez l'ordre de déclaration correct. Lorsque vous utilisez la propriété abrégée animation, déclarez animation-timeline après celle-ci pour éviter que la propriété abrégée ne réinitialise la chronologie.

Lorsque vous utilisez la fonction scroll() pour créer une animation basée sur le défilement :

  • FACULTATIF : soyez explicite concernant le conteneur de défilement. Lorsque vous ne ciblez pas le conteneur de défilement ancêtre le plus proche, indiquez explicitement celui que vous souhaitez utiliser avec scroll(root) ou scroll(self).
    • Lorsque root, nearest ou self ne suffisent pas, utilisez une chronologie de défilement nommée.
  • FACULTATIF : indiquez explicitement l'axe à suivre. Lorsque vous ne ciblez pas l'axe block par défaut (par exemple, dans un défilement horizontal), indiquez explicitement l'axe à suivre avec scroll(block) ou scroll(inline).

Lorsque vous utilisez la propriété scroll-timeline pour créer une animation basée sur le défilement :

  • À FAIRE : utilisez une <dashed-ident> CSS pour le nom.
  • FACULTATIF : Soyez explicite sur l'axe à suivre. Lorsque vous ne ciblez pas l'axe block par défaut (par exemple, dans un défilement horizontal), indiquez explicitement l'axe à suivre avec scroll-timeline-axis.
  • À FAIRE : Assurez-vous que le champ d'application de la recherche fonctionne. Lorsque l'élément qui déclare scroll-timeline n'est pas un ancêtre de l'arborescence plate de l'élément animé, augmentez la visibilité du nom de scroll-timeline en utilisant timeline-scope sur un ancêtre partagé.

Stratégies de remplacement

Pour les navigateurs qui ne sont pas compatibles avec les animations déclenchées par le défilement, vous pouvez utiliser une solution de remplacement pour recréer les effets visuels. Les solutions de remplacement sont généralement créées avec un écouteur de défilement (pour les effets ScrollTimeline) ou l'API IntersectionObserver (pour les effets ViewTimeline).

Dans les navigateurs compatibles de manière native avec les animations liées au défilement, utilisez TOUJOURS l'implémentation CSS native, car elle est plus performante.

Notez que tous les effets ne peuvent pas être recréés à l'aide de l'approche de remplacement.

Pour ce cas d'utilisation spécifique, le script suivant applique la solution de remplacement pour les navigateurs qui ne sont pas compatibles avec les animations déclenchées par le défilement. Il utilise un écouteur de défilement pour suivre la position de défilement de l'élément racine et met à jour la propriété transform de la barre de progression en conséquence.

<script>
  if (!CSS.supports('animation-timeline', 'scroll()')) {
    const progress = document.querySelector('#progress');

    window.addEventListener('scroll', () => {
      const scrollable = document.documentElement.scrollHeight - window.innerHeight;
      const scrolled = window.scrollY;
      const progressPercentage = (scrolled / scrollable);

      progress.style.transform = `scaleX(${progressPercentage})`;
    });
  }
</script>