W3docs

Animazioni JavaScript

Impara le animazioni JavaScript con requestAnimationFrame, funzioni di easing, concatenamento, Web Animations API e best practice per performance e accessibilità.

Le animazioni JavaScript offrono un modo dinamico per migliorare l'esperienza utente sulle pagine web. Dagli effetti sottili ai movimenti complessi, padroneggiare le animazioni JavaScript può elevare significativamente l'interattività e l'attrattiva visiva dei tuoi progetti. Questa guida completa fornirà un'esplorazione approfondita delle animazioni JavaScript, inclusi i concetti fondamentali, le tecniche essenziali e gli esempi pratici per aiutarti a diventare esperto nella creazione di animazioni coinvolgenti.

Introduzione alle animazioni JavaScript

Le animazioni JavaScript manipolano gli elementi del Document Object Model (DOM) per creare effetti visivi. Vengono usate per spostare, ridimensionare e modificare gli elementi in risposta alle interazioni dell'utente o automaticamente nel tempo. Comprendendo i principi fondamentali e le tecniche, puoi creare animazioni che siano sia performanti che visivamente accattivanti.

Perché usare JavaScript per le animazioni?

Le animazioni JavaScript offrono maggiore controllo e flessibilità rispetto alle animazioni CSS. Consentono sequenze e interazioni più complesse, rendendo possibile la creazione di effetti altamente personalizzati. Le animazioni JavaScript possono essere controllate dinamicamente e sono spesso necessarie quando si deve rispondere agli input dell'utente o integrare altre funzionalità JavaScript.

Come regola generale:

  • Usa CSS (transition/@keyframes) per semplici cambiamenti di stato dichiarativi come hover, dissolvenze e toggle. Il browser ottimizza questi su un thread compositor separato, quindi tendono a essere la scelta più performante. Vedi CSS animations.
  • Usa JavaScript quando l'animazione dipende da dati, gesti dell'utente, fisica o logica che CSS non può esprimere — ad esempio seguire il mouse, agganciarsi a posizioni dinamiche o concatenare molti passaggi con condizioni di diramazione.

Per l'opzione JavaScript con il minimo sforzo, la Web Animations API integrata (trattata di seguito) offre prestazioni di qualità CSS con controllo imperativo, mentre requestAnimationFrame fornisce il controllo completo fotogramma per fotogramma quando necessario.

Concetti di base delle animazioni JavaScript

Prima di addentrarsi nelle tecniche specifiche, è essenziale comprendere alcuni concetti di base che sono alla base delle animazioni JavaScript:

  1. Fotogrammi al secondo (FPS): La frequenza con cui vengono visualizzati i fotogrammi dell'animazione. Un FPS più elevato produce animazioni più fluide.
  2. Funzioni di easing: Queste funzioni controllano l'accelerazione e la decelerazione delle animazioni, aggiungendo un movimento naturale.
  3. Funzioni di temporizzazione: Determinano la durata e il ritardo delle animazioni.
  4. Keyframe: Definiscono gli stati iniziali, finali e intermedi di un'animazione.

requestAnimationFrame vs. setInterval

Prima che esistesse requestAnimationFrame, le animazioni erano guidate da setInterval e setTimeout, eseguendo un callback ogni pochi millisecondi. Questo approccio ha due problemi: l'intervallo non è sincronizzato con la frequenza di aggiornamento dello schermo (causando fotogrammi persi o doppi) e il timer continua a scattare anche quando la scheda è in background, sprecando CPU e batteria.

requestAnimationFrame(callback) risolve entrambi. Il browser chiama il tuo callback appena prima del prossimo repaint — di solito circa 60 volte al secondo, in sincronia con il display — e si mette automaticamente in pausa quando la scheda è nascosta. Il callback riceve un argomento timestamp ad alta risoluzione, che si usa per calcolare il progresso basandosi sul tempo reale trascorso anziché presumere una frequenza di fotogrammi fissa.

// setInterval: fixed timer, not synced to the screen, runs in background tabs
let pos = 0;
const id = setInterval(() => {
  pos += 2;
  if (pos >= 200) clearInterval(id);
}, 16);

// requestAnimationFrame: synced to repaint, pauses in background tabs
function frame(timestamp) {
  // use timestamp to drive the animation
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

Usa requestAnimationFrame per qualsiasi cosa che aggiorni i visual ad ogni fotogramma.

Esempio semplice di animazione JavaScript

Iniziamo con un semplice esempio in cui animiamo un quadrato che si muove attraverso lo schermo.

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: red;
      position: absolute;
      left: 0;
      top: 50px;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let pos = 0;

    function move() {
      if (pos < window.innerWidth - 50) {
        pos += 2;
        square.style.transform = 'translateX(' + pos + 'px)';
        requestAnimationFrame(move);
      }
    }

    move();
  </script>
</body>
</html>

Questo esempio dimostra un'animazione di base in cui un quadrato rosso si sposta dal lato sinistro dello schermo verso destra. L'animazione usa la funzione requestAnimationFrame, che garantisce un movimento fluido sincronizzando l'animazione con la frequenza di aggiornamento del display. La funzione move aggiorna incrementalmente la posizione del quadrato e continua a richiamare se stessa finché il quadrato non raggiunge il bordo della finestra.

Informazione

Sebbene le animazioni JS richiedano aggiornamenti frequenti degli stili, preferisci transform e opacity per evitare di innescare reflow del layout.

Tecniche avanzate nelle animazioni JavaScript

Utilizzo delle funzioni di easing

Le funzioni di easing rendono le animazioni più naturali variando la velocità dell'animazione. Di solito si aspettano un valore di progresso normalizzato tra 0 e 1. Ecco un esempio che usa una funzione di easing per animare il quadrato:

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: blue;
      position: absolute;
      left: 0;
      top: 50px;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let start = null;
    const duration = 2000; // Animation duration in milliseconds

    function easeOutQuad(t) {
      return t * (2 - t);
    }

    function animate(timestamp) {
      if (!start) start = timestamp;
      let elapsed = timestamp - start;
      let progress = Math.min(elapsed / duration, 1);
      let easedProgress = easeOutQuad(progress);

      square.style.transform = 'translateX(' + (window.innerWidth - 50) * easedProgress + 'px)';

      if (progress < 1) {
        requestAnimationFrame(animate);
      }
    }

    requestAnimationFrame(animate);
  </script>
</body>
</html>

Questo esempio arricchisce l'animazione semplice incorporando una funzione di easing chiamata easeOutQuad. Le funzioni di easing creano animazioni dall'aspetto più naturale regolando la velocità dell'elemento animato nel tempo. In questo caso, il quadrato blu si sposta da sinistra a destra, partendo velocemente e rallentando man mano che si avvicina alla fine. La durata dell'animazione è impostata a 2000 millisecondi e la posizione del quadrato viene aggiornata in base al progresso con easing.

Concatenamento delle animazioni

Il concatenamento delle animazioni consente di eseguire più animazioni in sequenza. Ecco un esempio di un quadrato che prima si sposta a destra, poi verso il basso:

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: green;
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let xPos = 0;
    let yPos = 0;

    function moveRight() {
      if (xPos < window.innerWidth - 50) {
        xPos += 2;
        square.style.transform = 'translateX(' + xPos + 'px)';
        requestAnimationFrame(moveRight);
      } else {
        xPos = 0;
        yPos = 0; // Explicitly reset yPos before downward phase
        square.style.transform = 'translateX(0px)';
        requestAnimationFrame(moveDown);
      }
    }

    function moveDown() {
      if (yPos < window.innerHeight - 50) {
        yPos += 2;
        square.style.transform = 'translateY(' + yPos + 'px)';
        requestAnimationFrame(moveDown);
      } else {
        yPos = 0; // Reset for repeated cycles
        requestAnimationFrame(moveRight);
      }
    }

    moveRight();
  </script>
</body>
</html>

Questo esempio dimostra il concatenamento delle animazioni spostando un quadrato verde prima verso destra e poi verso il basso. La funzione moveRight sposta il quadrato verso destra fino a raggiungere il bordo della finestra. Una volta raggiunto il bordo destro, yPos viene esplicitamente reimpostato a 0 prima che venga chiamata moveDown. Questa esecuzione sequenziale, combinata con una corretta gestione dello stato per i cicli ripetuti, mostra come creare sequenze di animazione più complesse concatenando più animazioni insieme.

Animazione di più proprietà

Animare più proprietà contemporaneamente può creare effetti più complessi e visivamente accattivanti. Ecco un esempio che cambia sia la posizione che il colore:

<!DOCTYPE html>
<html>
<head>
  <style>
    #square {
      width: 50px;
      height: 50px;
      background-color: purple;
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>
<body>
  <div id="square"></div>
  <script>
    const square = document.getElementById('square');
    let pos = 0;

    function changeColor(progress) {
      let red = Math.floor(255 * progress);
      let green = Math.floor(255 * (1 - progress));
      return `rgb(${red}, ${green}, 0)`;
    }

    function animate() {
      if (pos < window.innerWidth - 50) {
        pos += 2;
        let progress = pos / (window.innerWidth - 50);
        square.style.transform = 'translateX(' + pos + 'px)';
        square.style.backgroundColor = changeColor(progress);
        requestAnimationFrame(animate);
      }
    }

    animate();
  </script>
</body>
</html>

Questo esempio anima sia la posizione che il colore di sfondo di un quadrato viola. Man mano che il quadrato si sposta da sinistra a destra, il suo colore passa dal rosso al verde. La funzione changeColor calcola il colore in base al progresso dell'animazione, creando un effetto sfumato. Questo dimostra come animare più proprietà contemporaneamente, aggiungendo complessità e interesse visivo alle animazioni.

La Web Animations API

Creare manualmente un ciclo requestAnimationFrame offre il controllo totale, ma per la maggior parte delle animazioni basate su timeline il browser dispone già di un motore integrato: la Web Animations API (WAAPI). Chiamando element.animate(keyframes, options) si esegue l'animazione fuori dal thread principale quando possibile (proprio come CSS), ottenendo una riproduzione fluida senza dover scrivere un ciclo di fotogrammi. Restituisce inoltre un oggetto Animation che puoi mettere in pausa, invertire, cercare o attendere.

<!DOCTYPE html>
<html>
<head>
  <style>
    #box {
      width: 50px;
      height: 50px;
      background-color: teal;
      position: absolute;
      left: 0;
      top: 50px;
    }
  </style>
</head>
<body>
  <div id="box"></div>
  <script>
    const box = document.getElementById('box');

    const animation = box.animate(
      [
        { transform: 'translateX(0)', opacity: 1 },
        { transform: 'translateX(300px)', opacity: 0.3 }
      ],
      {
        duration: 2000,
        easing: 'ease-out',
        fill: 'forwards'
      }
    );

    // The returned object gives you imperative control:
    animation.finished.then(() => console.log('done'));
    // animation.pause();  animation.reverse();  animation.currentTime = 1000;
  </script>
</body>
</html>

Il primo argomento è un array di keyframe (ciascuno un object di proprietà CSS); il secondo imposta duration, easing e comportamenti come fill (mantieni lo stato finale), iterations, direction e delay. Poiché easing accetta gli stessi valori cubic-bezier() e le stesse parole chiave di CSS, spesso non è necessario scrivere la matematica dell'easing manualmente.

Preferisci WAAPI quando la tua animazione è una timeline fissa di cambiamenti di proprietà. Riserva un ciclo manuale requestAnimationFrame per i casi in cui ogni fotogramma dipende da input in tempo reale o stato calcolato — come fisica, trascinamento o valori non noti in anticipo.

Interruzione e inversione delle animazioni

Un ciclo requestAnimationFrame continua a girare finché non si smette di pianificare nuovi fotogrammi. Per annullarne uno immediatamente, salva l'ID che requestAnimationFrame restituisce e passalo a cancelAnimationFrame:

let rafId;

function loop(timestamp) {
  // ...update visuals...
  rafId = requestAnimationFrame(loop);
}

rafId = requestAnimationFrame(loop);

// Later, e.g. on a button click or when the element is removed:
cancelAnimationFrame(rafId);

Annulla sempre le animazioni in esecuzione prima di rimuovere i loro elementi dal DOM, altrimenti il ciclo continua a modificare un nodo staccato e perde lavoro ad ogni fotogramma. Con la Web Animations API gli equivalenti sono semplicemente animation.cancel(), animation.pause() e animation.reverse().

Accessibilità: rispettare la riduzione del movimento

Il movimento ampio o veloce può provocare nausea, vertigini o emicranie in alcuni utenti. I sistemi operativi espongono una preferenza "riduci movimento" e dovresti onorarla. In JavaScript, leggila con matchMedia e disabilita l'animazione oppure passa direttamente allo stato finale:

const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (prefersReducedMotion) {
  // Skip the animation: apply the final state instantly
  square.style.transform = 'translateX(300px)';
} else {
  requestAnimationFrame(animate);
}

Questo singolo controllo rende le tue interfacce sia più inclusive che più piacevoli per gli utenti che rinunciano al movimento.

Ottimizzazione delle prestazioni per le animazioni JavaScript

Per garantire animazioni fluide ed efficienti, segui questi suggerimenti per l'ottimizzazione delle prestazioni:

  1. Usa requestAnimationFrame: Questo metodo si sincronizza con la frequenza di aggiornamento del display, fornendo animazioni più fluide.
  2. Riduci al minimo le manipolazioni del DOM: Raggruppa gli aggiornamenti del DOM per ridurre reflow e repaint. Preferisci transform e opacity per le modifiche di posizione per sfruttare l'accelerazione GPU.
  3. Ottimizza il CSS: Usa proprietà CSS accelerate dalla GPU come transform e opacity.
  4. Limita gli eventi: Limita la frequenza di eventi come resize o scroll usando tecniche di throttling o debouncing.
  5. Interrompi le animazioni correttamente: Usa cancelAnimationFrame per fermare le animazioni quando non sono più necessarie. Salva l'ID dell'animazione e chiama cancelAnimationFrame(id) per prevenire perdite di memoria. Esempio:
    let animationId = requestAnimationFrame(animate);
    // ... later ...
    cancelAnimationFrame(animationId);
  6. Evita il layout thrashing: Non leggere le proprietà del layout (es. offsetWidth, getBoundingClientRect) all'interno del ciclo di animazione, poiché questo costringe il browser a ricalcolare gli stili a metà fotogramma e può causare jank.
Informazione

Puoi anche creare animazioni con solo CSS (senza JavaScript), e di solito porterà a prestazioni migliori. Vedi CSS animations.

Conclusione

Padroneggiare le animazioni JavaScript comporta la comprensione dei principi di base, l'implementazione di tecniche avanzate e l'ottimizzazione delle prestazioni. Seguendo questa guida, puoi creare animazioni coinvolgenti e reattive che migliorano l'esperienza utente. Sperimenta diverse funzioni di easing, concatenamento e animazioni di proprietà per sbloccare il pieno potenziale di JavaScript nei tuoi progetti web.

Per approfondire, esplora questi argomenti correlati:

Esercitazione

Pratica
Quali metodi e funzioni sono comunemente usati nelle animazioni JavaScript?
Quali metodi e funzioni sono comunemente usati nelle animazioni JavaScript?
Was this page helpful?