jQuery / .animate() — Créer des effets personnalisés

.animate() fait évoluer les propriétés CSS actuelles vers des cibles dans le temps. .animate({ left: 100, width: 160, opacity: 0.4 }) change position, largeur et opacité ensemble. Durée et easing règlent le parcours ; la fin est appelée par élément.

Propriétés et durée

Le premier argument .animate() contient les cibles. left et width numériques sont ici des pixels ; opacity est sans unité entre zéro et un. Un objet évolue simultanément, contrairement aux appels distincts placés dans la file habituelle.

left nécessite un positionnement autre que static. relative décale depuis l’origine, pas vers x=100 du document. width mesure le contenu ; content-box simple évite d’ajouter bordure et padding à la comparaison.

duration est en millisecondes, 400 par défaut. Easing change la vitesse : swing et linear atteignent les mêmes cibles. Ne supposez pas l’interpolation de toute couleur ou chaîne transform par jQuery seul ; vérifiez les propriétés numériques prises en charge.

Exemple complet avec deux éléments

Enregistrez UTF-8 animate-demo.html et ouvrez-le. Internet charge la version complète officielle 4.0.0. Les animations temporisées nécessitent effects, pas Slim. Aucun serveur ou paquet n’est nécessaire. Valeurs intermédiaires, cibles, arrêt et répétition ont été vérifiés dans Edge sous Windows avec 3.7.1 et 4.0.0.

Les boîtes ont les mêmes cibles. Start rétablit les valeurs initiales. Reset conserve durée/easing, restaure boîtes et compteurs. Un second départ est bloqué pendant l’exécution.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Animate CSS properties and observe completion</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 20px; }
    label { display: block; margin: 8px 0; }
    select, button { font: inherit; margin: 4px; }
    #stage { background: #eef4f4; padding: 10px; overflow-x: auto; }
    .box { position: relative; left: 0; width: 80px; height: 45px; margin: 12px 0; background: teal; color: white; box-sizing: content-box; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <label>Duration <select id="duration"><option value="600">600ms</option><option value="1200">1200ms</option><option value="0">0ms</option></select></label>
  <label>Easing <select id="easing"><option value="swing">swing</option><option value="linear">linear</option></select></label>
  <button type="button" id="start">Start</button>
  <button type="button" id="stop">Stop here</button>
  <button type="button" id="reset">Reset</button>
  <div id="stage"><div class="box" id="alpha">Alpha</div><div class="box" id="beta">Beta</div></div>
  <pre id="result" aria-live="polite"></pre>
  <script>
    jQuery(function ($) {
      const $boxes = $("#stage > .box");
      let generation = 0, busy = false, state = "ready", completed = 0, batches = 0;
      function report() {
        const values = $boxes.map(function () {
          const $box = $(this);
          return this.id + ": left=" + Math.round(parseFloat($box.css("left"))) + "; width=" + Math.round($box.width()) + "; opacity=" + Number(parseFloat($box.css("opacity")).toFixed(2));
        }).get();
        $("#result").text(["state=" + state, "completed=" + completed + "; batches=" + batches, ...values].join("\n"));
      }
      function reset() {
        generation++; $boxes.stop(true, false).css({ left: 0, width: 80, opacity: 1 });
        busy = false; state = "ready"; completed = batches = 0;
        $("#start").prop("disabled", false); report();
      }
      $("#start").on("click", function () {
        if (busy) return;
        reset(); busy = true; state = "running"; $(this).prop("disabled", true);
        const run = generation;
        const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
        const duration = reduce ? 0 : Number($("#duration").val());
        $boxes.animate({ left: 100, width: 160, opacity: 0.4 }, {
          duration: duration, easing: $("#easing").val(),
          complete: function () { if (run === generation) { completed++; report(); } }
        });
        $boxes.promise("fx").done(function () {
          if (run !== generation) return;
          batches++; busy = false; state = "done";
          $("#start").prop("disabled", false); report();
        });
        report();
      });
      $("#stop").on("click", function () {
        if (!busy) return;
        generation++; $boxes.stop(true, false);
        busy = false; state = "stopped"; $("#start").prop("disabled", false); report();
      });
      $("#reset").on("click", reset);
      reset();
    });
  </script>
</body>
</html>

Départ : largeur 80, left relatif zéro, opacité un. L’arrondi affiché ne limite pas le calcul aux entiers.

state=ready
completed=0; batches=0
alpha: left=0; width=80; opacity=1
beta: left=0; width=80; opacity=1
  1. Start avec 600ms et swing : trois propriétés évoluent ensemble.
  2. Fin : state=done, completed=2; batches=1 ; chaque boîte vaut left=100; width=160; opacity=0.4.
  3. Reset, puis changez seulement easing en linear et comparez. Les cibles restent identiques.
  4. À 1200ms, Stop here fige les valeurs intermédiaires avec state=stopped, sans done ultérieur.
  5. Reset puis rejouez. 0ms atteint immédiatement cibles et compteurs.
  6. Le mouvement réduit termine aussi immédiatement malgré 1200ms choisi.

Fin individuelle et collective

complete est appelé deux fois ; this est l’élément DOM concerné. Un rappel ne signifie pas fin collective. .animate() ne bloque pas JavaScript jusqu’au terme.

.promise("fx") observe le travail de la collection. Ici batches augmente une fois et Start revient. Des tâches supplémentaires dans la file standard peuvent prolonger l’attente.

Une file vide ne prouve pas une fin naturelle. .stop(true, false) peut la vider à mi-parcours et résoudre la promesse fx. Stop et Reset changent d’abord generation pour empêcher les anciens rappels de remplacer l’annulation par done.

Les appels répétés empilent normalement des effets ; désactivation et busy les évitent. Rediriger depuis la position actuelle nécessite une autre politique explicite.

Arrêt, restauration et disposition

jumpToEnd=false conserve les valeurs actuelles. Reset fixe les trois propriétés ; sinon les anciens intermédiaires changeraient la comparaison.

prefers-reduced-motion reduce met cette exécution à zéro avec même cible et fin, sans changer les réglages globaux.

Position et largeur affectent voisinage et chevauchements : vérifiez écran étroit, zoom, texte long et conteneurs défilants. Opacité zéro n’implique ni display:none ni état caché pour l’accessibilité.

Évitez qu’une transition CSS ou un plugin pilote simultanément les mêmes propriétés. Définissez responsables des éléments, propriétés et files, et limitez sélecteurs et événements à la fonction.