Aller au contenu
← 1001 Widgets
N° 017 · ●○○

Barre de lecture

Une barre qui avance avec le défilement et un temps de lecture qui diminue. Le bon exercice pour apprendre à mesurer le scroll sans ralentir la page.

CSSJS
Ce qu'on apprend
  • Calculer la part défilée d'un élément
  • Animer avec transform plutôt que width
  • Limiter le travail avec requestAnimationFrame
Pourquoi ce langage

Pourquoi une barre de lecture ? Sur un long article, on ne sait jamais combien il reste. Une fine barre en haut de l'écran répond à la question sans un mot.

Le calcul tient en une ligne : la distance déjà défilée, divisée par la distance qu'on peut défiler au total. On obtient un nombre entre 0 et 1.

Pourquoi en JavaScript simple ? Un écouteur de défilement, une division, un style. Le vrai sujet est ailleurs : ne rien faire de lourd pendant qu'on défile.

Comment ça marche
  1. LE HTML

    <div class="barre" aria-hidden="true"><i id="progression"></i></div>
    <article id="article">...</article>
  2. LE CSS

    La barre est pleine par défaut, et réduite avec scaleX : pas de recalcul de mise en page.

    .barre { position: fixed; top: 0; left: 0; right: 0; height: 4px; }
    .barre i { display: block; height: 100%; background: currentColor; transform-origin: left; transform: scaleX(0); }
  3. LE CALCUL

    scrollY : ce qu'on a défilé. scrollHeight - innerHeight : ce qu'on peut défiler.

    function part() {
      const max = document.documentElement.scrollHeight - innerHeight;
      return max > 0 ? Math.min(1, scrollY / max) : 1;
    }
  4. METTRE À JOUR UNE FOIS PAR IMAGE

    Le défilement envoie des dizaines d'événements : on ne dessine qu'une fois par image.

    let enAttente = false;
    addEventListener('scroll', () => {
      if (enAttente) return;
      enAttente = true;
      requestAnimationFrame(() => {
        document.getElementById('progression').style.transform = `scaleX(${part()})`;
        enAttente = false;
      });
    }, { passive: true });
  5. LE TEMPS RESTANT

    Environ 230 mots par minute pour une lecture à l'écran.

    const mots = document.getElementById('article').textContent.split(/\s+/).length;
    const minutesRestantes = () => Math.ceil(mots * (1 - part()) / 230);
Pièges

PIÈGE 1 : Animer width

Piège

Mauvais : barre.style.width = pourcent + '%'. À chaque défilement, le navigateur recalcule la mise en page.

Fix

Correct : transform: scaleX(), qui ne touche qu'à l'affichage.

PIÈGE 2 : Tout faire dans l'écouteur

Piège

Mauvais : calculer et dessiner à chaque événement scroll. Sur mobile, le défilement saccade.

Fix

Correct : requestAnimationFrame pour dessiner au plus une fois par image, et { passive: true } sur l'écouteur.

PIÈGE 3 : Diviser par zéro

Piège

Mauvais : scrollY / max sur une page plus courte que l'écran. max vaut 0, le résultat vaut NaN.

Fix

Correct : tester max > 0, et considérer qu'une page courte est lue en entier.

PERSONNALISER
  • Mesurez l'article seulement, et non toute la page, avec sa position getBoundingClientRect().
  • Affichez « Lu » et un petit pictogramme quand la barre est pleine.
  • Une barre verticale sur le côté pour un format magazine.
OPTIMISATION
  • Les navigateurs récents savent le faire en CSS pur avec animation-timeline: scroll(), sans une ligne de JavaScript.
  • Gardez le JavaScript comme solution de repli, détectée avec CSS.supports.
VERS REACT

Un hook useScrollProgress renvoie la valeur entre 0 et 1.

L'écouteur s'ajoute dans un useEffect et se retire au démontage.

Pour éviter un rendu par image, écrivez directement dans un useRef sur l'élément au lieu d'un useState.

Commentaires
0/1000

Chargement des commentaires…