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.
- Enchaîner des étapes avec setTimeout
- Découper une phrase avec slice
- Respecter prefers-reduced-motion
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.
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>
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');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); }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; } }LANCER, SAUF SI L'UTILISATEUR PRÉFÈRE LE CALME
if (matchMedia('(prefers-reduced-motion: reduce)').matches) txt.textContent = PHRASES[0]; else pas();
PIÈGE 1 : setInterval à vitesse fixe
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.
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
Mauvais : annoncer chaque lettre une par une avec aria-live. C'est insupportable à écouter.
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
Mauvais : relancer pas() à chaque clic sur Rejouer. Deux machines écrivent dans le même texte.
Correct : garder l'identifiant du setTimeout et appeler clearTimeout avant de relancer.
- 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.
- 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.
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).
Chargement des commentaires…