Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

Les animations JavaScript

Les animations JavaScript. Dans cette leçon du cours JavaScript, vous apprendrez à : connaître les quatre techniques d'animation d'une page web et savoir choisir ; déclencher une transition CSS depuis...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Les animations JavaScript. Dans cette leçon du cours JavaScript, vous apprendrez à : connaître les quatre techniques d'animation d'une page web et savoir choisir ; déclencher une transition CSS depuis JavaScript ; écrire une animation avec requestAnimationFrame, une durée et une fonction d'accélération ; piloter une animation avec la Web Animations API.

Chapitre 15 — Animations et graphismes avec Canvas · 15.1 Les animations JavaScript

🎯 Objectifs de la leçon
• connaître les quatre techniques d'animation d'une page web et savoir choisir ;
• déclencher une transition CSS depuis JavaScript ;
• écrire une animation avec requestAnimationFrame, une durée et une fonction d'accélération ;
• piloter une animation avec la Web Animations API.

1. Qu'est-ce qu'une animation ?

Une animation est une suite d'images affichées assez vite pour que l'œil voie un mouvement continu. Un écran classique se rafraîchit 60 fois par seconde, soit une image toutes les 16,7 ms (certains écrans montent à 120 ou 144 Hz). Animer, c'est modifier un peu ce qui est affiché à chaque image.

Comparaison des techniques d'animation
Figure 1 — Quatre techniques, du plus simple au plus puissant.

2. Le plus simple : une transition CSS déclenchée par JavaScript

Pour un changement d'état (afficher un panneau, survoler un bouton), CSS sait animer tout seul. JavaScript se contente d'ajouter ou de retirer une classe :

📄 l1_transition.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <style>
    #panneau {
      width: 260px; padding: 16px; background: #e3f2fd; border-radius: 10px; font-family: sans-serif;
      margin-top: 12px;
      opacity: 0; transform: translateY(-20px);
      pointer-events: none; /* invisible ≠ absent : sans cela, il bloquerait les clics */
      transition: opacity 0.4s, transform 0.4s ease-out; /* CSS se charge de l'animation */
    }
    #panneau.visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
  </style>
</head>
<body>
  <button id="bouton">Afficher / masquer</button>
  <div id="panneau">Je glisse et j'apparais en douceur.</div>
  <script>
    // JavaScript se contente de changer une classe
    document.querySelector("#bouton").addEventListener("click", () => {
      document.querySelector("#panneau").classList.toggle("visible");
    });
  </script>
</body>
</html>

Résultat dans le navigateur
Résultat dans le navigateur

⚠️ Attention
Un élément à opacity: 0 est invisible mais toujours présent: il peut recouvrir un bouton et intercepter les clics. D'où pointer-events: none tant qu'il est caché. Nous avons rencontré ce bug en testant cet exemple !

3. L'ancienne méthode : setInterval

📄 l1_setinterval.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <style>
    #piste { position: relative; height: 60px; background: #eceff1; }
    #boite { position: absolute; width: 50px; height: 50px; top: 5px; background: #1565c0; border-radius: 8px; }
  </style>
</head>
<body>
  <div id="piste"><div id="boite"></div></div>
  <script>
    // Ancienne méthode : setInterval toutes les 16 ms (≈ 60 images/s)
    const boite = document.querySelector("#boite");
    let x = 0;
    const id = setInterval(() => {
      x += 4;
      boite.style.transform = `translateX(${x}px)`;
      if (x >= 400) clearInterval(id);
    }, 16);
  </script>
</body>
</html>

La boîte avance de 4 px toutes les 16 ms
La boîte avance de 4 px toutes les 16 ms

Cela fonctionne, mais :

  • le minuteur n'est pas synchronisé avec l'écran : certaines images sont sautées ou affichées deux fois (saccades) ;
  • la vitesse dépend de la charge de l'ordinateur ;
  • il continue de tourner quand l'onglet est caché, et consomme de la batterie.

4. La bonne méthode : requestAnimationFrame

requestAnimationFrame(fonction) demande au navigateur d'appeler fonction juste avant la prochaine image. La fonction reçoit l'heure précise (en ms). Pour continuer l'animation, elle redemande une image : c'est une boucle.

📄 l1_raf.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <style>
    #piste { position: relative; height: 60px; background: #eceff1; }
    #boite { position: absolute; width: 50px; height: 50px; top: 5px; background: #2e7d32; border-radius: 8px; }
  </style>
</head>
<body>
  <div id="piste"><div id="boite"></div></div>
  <button id="lancer">Lancer</button>
  <script>
    const boite = document.querySelector("#boite");
    const DISTANCE = 400; // pixels
    const DUREE = 1000;   // millisecondes

    // Fonction d'accélération : ralentit en fin de course
    const easeOut = (t) => 1 - (1 - t) ** 3;

    function animer() {
      let debut = null;

      function etape(temps) {           // temps : fourni par le navigateur, en ms
        if (debut === null) debut = temps;
        const progression = Math.min((temps - debut) / DUREE, 1); // de 0 à 1
        boite.style.transform = `translateX(${DISTANCE * easeOut(progression)}px)`;
        if (progression < 1) requestAnimationFrame(etape); // image suivante
      }

      requestAnimationFrame(etape);
    }

    document.querySelector("#lancer").addEventListener("click", animer);
  </script>
</body>
</html>

Cinq instants de l'animation (0, 150, 350, 700 et 1 100 ms) : la boîte ralentit en arrivant
Cinq instants de l'animation (0, 150, 350, 700 et 1 100 ms) : la boîte ralentit en arrivant

  • On calcule une progression de 0 à 1 à partir du temps écoulé : l'animation dure 1 s quel que soit l'écran.
  • La fonction d'accélération (easing) easeOut transforme cette progression pour que le mouvement ralentisse à la fin, plus naturellement.
  • On anime transform plutôt que left: le navigateur n'a pas à recalculer la mise en page, c'est beaucoup plus fluide.

5. La Web Animations API

element.animate(imagesClés, options) combine la simplicité des animations CSS et le contrôle de JavaScript :

📄 l1_waapi.html

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <style>
    #carte { width: 160px; padding: 20px; margin: 30px; background: #fff3e0;
             border: 2px solid #ef6c00; border-radius: 12px; font-family: sans-serif; }
  </style>
</head>
<body>
  <div id="carte">Nouveau message !</div>
  <button id="secouer">Secouer</button>
  <button id="pause">Pause / reprise</button>
  <script>
    const carte = document.querySelector("#carte");

    // Web Animations API : les images clés de CSS, pilotées en JavaScript
    const animation = carte.animate(
      [
        { transform: "translateX(0)" },
        { transform: "translateX(-12px)" },
        { transform: "translateX(12px)" },
        { transform: "translateX(0)" },
      ],
      { duration: 400, iterations: 3, easing: "ease-in-out" }
    );
    animation.pause(); // créée en pause

    document.querySelector("#secouer").addEventListener("click", () => {
      animation.currentTime = 0;
      animation.play();
    });
    document.querySelector("#pause").addEventListener("click", () => {
      animation.playState === "running" ? animation.pause() : animation.play();
    });
    animation.finished.then(() => console.log("Animation terminée")); // c'est une promesse !
  </script>
</body>
</html>

Après un clic sur « Secouer », la console affiche Animation terminée: animation.finished est une promesse (chapitre 18).

ℹ️ À retenir
Certaines personnes ont le mal des transports devant les animations. Respectez leur réglage système : if (matchMedia("(prefers-reduced-motion: reduce)").matches) { … animation réduite … }
ℹ️ À retenir
• Changement d'état simple → transition CSS + classList; images clés pilotées → element.animate().
• Animation calculée image par image → requestAnimationFrame, jamais setInterval.
• Basez-vous sur le temps écoulé (progression de 0 à 1), pas sur le nombre d'images.
• Animez transform et opacity; respectez prefers-reduced-motion.

🛠 Activité 1 — Une barre de notifications animée

Mission : Créez une page avec un bouton « Nouvelle notification » qui fait apparaître un bandeau en haut de l'écran pendant 3 secondes.

Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :

  1. Faites d'abord apparaître le bandeau avec une transition CSS. Quelles propriétés animez-vous ? Pourquoi pas top ou height?
  2. Comment le faire disparaître automatiquement après 3 s ? Que se passe-t-il si on clique trois fois rapidement ? Comment éviter ce problème ?
  3. Réécrivez l'animation avec requestAnimationFrame. Écrivez trois fonctions d'accélération (lineaire, easeIn, easeOut) et comparez-les visuellement.
  4. Comptez combien de fois votre fonction est appelée en 1 seconde (avec un compteur et console.log). Le résultat est-il le même sur un autre écran ou un téléphone ?
  5. Réécrivez la même animation avec element.animate(). Combien de lignes avez-vous économisé ? Ajoutez un bouton qui inverse l'animation (cherchez animation.reverse()).
  6. Activez « Réduire les animations » dans les réglages de votre système. Adaptez votre page pour qu'elle affiche le bandeau sans mouvement dans ce cas.

Je vérifie / je rends :

  • ☐ La page avec les trois versions (CSS, rAF, WAAPI).
  • ☐ Votre tableau comparatif : nombre de lignes, fluidité, contrôle.

15.2 Dessiner des graphismes avec l'API <canvas> →