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.
- Calculer la part défilée d'un élément
- Animer avec transform plutôt que width
- Limiter le travail avec requestAnimationFrame
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.
LE HTML
<div class="barre" aria-hidden="true"><i id="progression"></i></div> <article id="article">...</article>
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); }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; }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 });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ÈGE 1 : Animer width
Mauvais : barre.style.width = pourcent + '%'. À chaque défilement, le navigateur recalcule la mise en page.
Correct : transform: scaleX(), qui ne touche qu'à l'affichage.
PIÈGE 2 : Tout faire dans l'écouteur
Mauvais : calculer et dessiner à chaque événement scroll. Sur mobile, le défilement saccade.
Correct : requestAnimationFrame pour dessiner au plus une fois par image, et { passive: true } sur l'écouteur.
PIÈGE 3 : Diviser par zéro
Mauvais : scrollY / max sur une page plus courte que l'écran. max vaut 0, le résultat vaut NaN.
Correct : tester max > 0, et considérer qu'une page courte est lue en entier.
- 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.
- 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.
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.
Chargement des commentaires…