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

Effet machine à écrire

Un texte qui s'écrit lettre par lettre, s'efface et passe à la phrase suivante. Le bon exercice pour apprendre à enchaîner des étapes dans le temps.

JS
Ce qu'on apprend
  • Enchaîner des étapes avec setTimeout
  • Découper une phrase avec slice
  • Respecter prefers-reduced-motion
Pourquoi ce langage

Pourquoi une machine à écrire ? Parce que c'est l'effet le plus demandé sur les pages d'accueil, et qu'il apprend une chose essentielle : faire avancer un état pas à pas dans le temps.

Le principe : un index de lettre qui monte, puis qui redescend, puis une phrase suivante. Chaque pas programme le suivant avec setTimeout.

Pourquoi en JavaScript simple ? Deux compteurs et un minuteur. Pas besoin de bibliothèque pour un effet qui tient en quinze lignes.

Comment ça marche
  1. LE HTML

    Le texte et un curseur séparé, pour que le curseur clignote sans être réécrit.

    <p class="ecran" aria-live="polite"><span id="txt"></span><span class="curseur" aria-hidden="true"></span></p>
  2. L'ÉTAT

    const PHRASES = ['Bonjour, je suis Lass.', 'Je construis des sites qui bougent.'];
    let i = 0;         // la phrase en cours
    let j = 0;         // le nombre de lettres affichées
    let efface = false;
    const txt = document.getElementById('txt');
  3. UN PAS

    On ajoute ou on retire une lettre, puis on programme le pas suivant.

    function pas() {
      const phrase = PHRASES[i];
      j += efface ? -1 : 1;
      txt.textContent = phrase.slice(0, j);
      if (!efface && j === phrase.length) { efface = true; return setTimeout(pas, 1400); }
      if (efface && j === 0) { efface = false; i = (i + 1) % PHRASES.length; }
      setTimeout(pas, efface ? 35 : 70 + Math.random() * 40);
    }
  4. LE CURSEUR

    steps(1) donne un clignotement net, sans fondu.

    .curseur { display: inline-block; width: .5em; height: .9em; background: currentColor; animation: clignote 1s steps(1) infinite; }
    @keyframes clignote { 50% { opacity: 0; } }
  5. LANCER, SAUF SI L'UTILISATEUR PRÉFÈRE LE CALME

    if (matchMedia('(prefers-reduced-motion: reduce)').matches) txt.textContent = PHRASES[0];
    else pas();
Pièges

PIÈGE 1 : setInterval à vitesse fixe

Piège

Mauvais : un setInterval de 100 ms. L'écriture paraît mécanique, et on ne peut pas faire de pause à la fin d'une phrase.

Fix

Correct : setTimeout à chaque pas, avec un délai qui varie un peu et une vraie pause en fin de phrase.

PIÈGE 2 : Le texte invisible pour les lecteurs d'écran

Piège

Mauvais : annoncer chaque lettre une par une avec aria-live. C'est insupportable à écouter.

Fix

Correct : aria-live="polite" sur un conteneur, ou mieux, la phrase complète en texte masqué visuellement.

PIÈGE 3 : Deux boucles en même temps

Piège

Mauvais : relancer pas() à chaque clic sur Rejouer. Deux machines écrivent dans le même texte.

Fix

Correct : garder l'identifiant du setTimeout et appeler clearTimeout avant de relancer.

PERSONNALISER
  • Une vitesse réglable avec un <input type="range"> lu à chaque pas.
  • Un bouton pause qui arrête la planification sans perdre la position.
  • Une couleur différente pour un mot clé : découpez la phrase en morceaux avec chacun son style.
OPTIMISATION
  • Avec prefers-reduced-motion, affichez directement la phrase entière.
  • Mettez la boucle en pause quand l'onglet est caché, avec l'événement visibilitychange.
VERS REACT

Les compteurs i, j et efface deviennent un seul useState ou un useReducer.

Le useEffect programme le pas suivant et renvoie clearTimeout comme nettoyage.

Le texte affiché se calcule pendant le rendu : phrase.slice(0, j).

Variantes
Commentaires
0/1000

Chargement des commentaires…