Skip to content
← 1001 Widgets
N° 035 · ●●○

Lap stopwatch

A stopwatch to the hundredth of a second, with a lap list where the fastest and slowest stand out. The right exercise to measure time precisely instead of counting intervals.

JS
What you'll learn
  • Measure time with performance.now
  • Pause without losing elapsed time
  • Compare a series to find the minimum and maximum
Why this language

Pourquoi un chronomètre avec tours ? Pour une séance de course, des longueurs à la piscine ou un entraînement par intervalles, on veut le temps total et le temps de chaque tour.

L'erreur classique serait d'ajouter dix millisecondes à chaque tic d'un setInterval. Les navigateurs retardent ces tics, et le chrono dérive. On note plutôt l'heure exacte du départ avec performance.now, et on calcule la différence à chaque image. La pause ajoute le temps déjà écoulé dans un cumul.

Pourquoi en JavaScript simple ? Deux nombres, une soustraction, et requestAnimationFrame pour l'affichage. La barre d'espace lance et arrête.

How it works
  1. L'ÉTAT

    cumul garde le temps des périodes précédentes, depart le début de la période en cours.

    let depart = 0, cumul = 0, enCours = false, raf = 0;
    const tours = [];
    let dernierTour = 0;
  2. LE TEMPS ÉCOULÉ

    performance.now est précis et ne recule pas si l'horloge de l'ordinateur change.

    const ecoule = () => enCours ? cumul + performance.now() - depart : cumul;
  3. FORMATER

    83456 ms devient « 01:23,45 ».

    function format(ms) {
      const p = n => String(n).padStart(2, '0');
      return p(Math.floor(ms / 60000)) + ':' + p(Math.floor(ms / 1000) % 60) + ',' + p(Math.floor(ms / 10) % 100);
    }
  4. DÉMARRER ET ARRÊTER

    function basculer() {
      if (enCours) { cumul = ecoule(); enCours = false; cancelAnimationFrame(raf); }
      else { depart = performance.now(); enCours = true; boucle(); }
    }
    function boucle() {
      document.getElementById('temps').textContent = format(ecoule());
      raf = requestAnimationFrame(boucle);
    }
  5. LES TOURS

    Un tour = le temps depuis le tour précédent. On repère le meilleur et le plus lent.

    function tour() {
      const t = ecoule();
      tours.push({ duree: t - dernierTour, total: t });
      dernierTour = t;
      const durees = tours.map(x => x.duree);
      const meilleur = Math.min(...durees), pire = Math.max(...durees);
      // on marque ensuite chaque ligne selon qu'elle vaut meilleur ou pire
    }
Pitfalls

PIÈGE 1 : Compter les tics

Pitfall

Mauvais : setInterval(() => ms += 10, 10). Les tics arrivent en retard, surtout dans un onglet en arrière-plan, et le chrono perd des secondes.

Fix

Correct : noter l'heure de départ et calculer la différence à chaque affichage.

PIÈGE 2 : Utiliser Date.now

Pitfall

Mauvais : Date.now pour mesurer une durée. Si l'ordinateur ajuste son heure pendant la course, le temps fait un saut.

Fix

Correct : performance.now, une horloge qui avance toujours régulièrement.

PIÈGE 3 : Remettre à zéro pendant la course

Pitfall

Mauvais : laisser le bouton actif quand le chrono tourne. Un doigt qui glisse et la séance est perdue.

Fix

Correct : désactiver la remise à zéro tant que le chrono n'est pas arrêté.

PERSONNALISER
  • Un mode intervalles : 30 secondes d'effort, 15 de repos, avec un signal sonore.
  • L'export des tours en CSV pour les reprendre dans un tableur.
  • Une vibration au téléphone à chaque tour avec navigator.vibrate.
OPTIMISATION
  • Quand l'onglet est caché, requestAnimationFrame s'arrête tout seul : le temps continue d'être juste grâce au calcul par différence.
  • Mettez le temps dans un élément role="timer" mais sans aria-live, pour ne pas saturer les lecteurs d'écran.
VERS REACT

Le départ et le cumul vivent dans des useRef : ils changent sans avoir besoin d'un nouveau rendu.

Le temps affiché est un useState mis à jour dans la boucle, ou mieux, écrit directement dans le DOM par une ref.

Les tours sont un useState tableau ; le meilleur et le pire se calculent au rendu.

Comments
0/1000

Loading comments…

Audience measurement

With your consent, this site measures its traffic with Google Analytics and the Meta pixel, via Google Tag Manager. Nothing is loaded until you choose, and you can change your mind at any time with the "Cookies" link at the bottom of the page.