Ce que vous allez créer
L’effet de machine à écrire (typewriter effect) affiche un texte caractère par caractère, comme si quelqu’un le tapait en direct. C’est un effet visuel populaire pour les pages d’accueil, les portfolios et les landing pages. Dans ce tutoriel, vous allez construire 3 versions progressives : une basique, une avec suppression et réécriture, et une version réutilisable avec des options configurables.
Version 1 : l’effet typewriter basique
Le principe est simple : on prend une chaîne de caractères, on la découpe, et on ajoute un caractère à la fois dans un élément HTML avec un setTimeout ou setInterval.
Le HTML
<div class="typewriter-container">
<h1 id="typewriter"></h1>
<span class="cursor">|</span>
</div>
Le CSS pour le curseur clignotant
.typewriter-container {
font-family: 'Courier New', monospace;
font-size: 2rem;
display: flex;
align-items: center;
}
.cursor {
animation: blink 0.7s infinite;
font-weight: 100;
margin-left: 2px;
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
Le JavaScript
function typeWriter(element, text, speed = 80) {
let index = 0;
function taper() {
if (index < text.length) {
element.textContent += text.charAt(index);
index++;
setTimeout(taper, speed);
}
}
taper();
}
// Lancer l'effet
const el = document.getElementById('typewriter');
typeWriter(el, 'Bienvenue sur ITSkillsCenter', 80);
Comment ça fonctionne : la fonction taper() s’appelle elle-même via setTimeout à chaque itération. À chaque appel, elle ajoute un caractère au contenu de l’élément et incrémente l’index. Quand l’index atteint la longueur du texte, elle s’arrête.
Version 2 : écrire, effacer et réécrire (effet boucle)
Cette version affiche un mot, l’efface, puis affiche le suivant. Parfait pour afficher plusieurs compétences ou services sur une page d’accueil.
function typewriterLoop(element, words, typeSpeed = 100, eraseSpeed = 50, pauseTime = 2000) {
let wordIndex = 0;
let charIndex = 0;
let isErasing = false;
function animate() {
const currentWord = words[wordIndex];
if (!isErasing) {
// Mode écriture
element.textContent = currentWord.substring(0, charIndex + 1);
charIndex++;
if (charIndex === currentWord.length) {
// Mot complet → pause puis effacer
isErasing = true;
setTimeout(animate, pauseTime);
return;
}
setTimeout(animate, typeSpeed);
} else {
// Mode suppression
element.textContent = currentWord.substring(0, charIndex - 1);
charIndex--;
if (charIndex === 0) {
// Mot effacé → passer au suivant
isErasing = false;
wordIndex = (wordIndex + 1) % words.length;
setTimeout(animate, 500);
return;
}
setTimeout(animate, eraseSpeed);
}
}
animate();
}
// Utilisation
const el = document.getElementById('typewriter');
typewriterLoop(el, [
'Développeur Web',
'Designer UI/UX',
'Freelance à Dakar',
'Passionné de code'
]);
Points clés :
wordIndexcycle entre les mots grâce à l’opérateur modulo%- L’effacement est plus rapide que l’écriture (50ms vs 100ms) pour un rendu naturel
- Une pause de 2 secondes après chaque mot complet laisse le temps de lire
Version 3 : composant réutilisable avec Promesses
Cette version utilise les Promesses et async/await pour un code plus propre et plus facile à étendre.
class Typewriter {
constructor(element, options = {}) {
this.element = element;
this.speed = options.speed || 80;
this.deleteSpeed = options.deleteSpeed || 40;
this.pauseTime = options.pauseTime || 1500;
}
// Attendre un certain temps
wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Écrire un texte caractère par caractère
async type(text) {
for (const char of text) {
this.element.textContent += char;
await this.wait(this.speed + Math.random() * 50); // Variation pour un effet naturel
}
return this;
}
// Effacer n caractères
async delete(count) {
for (let i = 0; i < count; i++) {
this.element.textContent = this.element.textContent.slice(0, -1);
await this.wait(this.deleteSpeed);
}
return this;
}
// Effacer tout
async deleteAll() {
await this.delete(this.element.textContent.length);
return this;
}
// Pause
async pause(ms) {
await this.wait(ms || this.pauseTime);
return this;
}
// Boucle sur plusieurs textes
async loop(texts) {
while (true) {
for (const text of texts) {
await this.type(text);
await this.pause();
await this.deleteAll();
await this.wait(300);
}
}
}
}
// Utilisation simple et lisible
const tw = new Typewriter(document.getElementById('typewriter'), {
speed: 70,
deleteSpeed: 30,
pauseTime: 2000
});
tw.loop([
'Je crée des sites web.',
'Je développe des applications.',
'Je transforme vos idées en code.'
]);
Pourquoi cette version est meilleure
- Lisible : le chaînage avec
async/awaitlit comme une recette étape par étape - Naturelle :
Math.random() * 50ajoute une légère variation de vitesse, comme un vrai humain - Extensible : ajoutez facilement des méthodes (
highlight(),moveTo(), etc.) - Réutilisable : un seul
new Typewriter()par élément
Ajouter un curseur CSS personnalisé
Pour un curseur plus réaliste que le simple |, utilisez un ::after pseudo-élément :
#typewriter::after {
content: '';
display: inline-block;
width: 3px;
height: 1.2em;
background-color: #333;
margin-left: 4px;
vertical-align: text-bottom;
animation: blink 0.7s step-end infinite;
}
@keyframes blink {
50% { opacity: 0; }
}
Avec cette approche, pas besoin d’élément HTML supplémentaire pour le curseur.
Exercice : créez votre propre effet
Créez une page HTML avec un effet typewriter qui :
- Écrit « Bonjour, je suis » puis enchaîne avec votre nom en couleur différente
- Pause 3 secondes
- Efface uniquement votre nom
- Écrit votre titre professionnel à la place
- Recommence en boucle
Indice : utilisez deux éléments <span> côte à côte — un fixe pour « Bonjour, je suis » et un dynamique pour le texte qui change. Appliquez la classe Typewriter uniquement sur le span dynamique.