Movimento

  • Utilizza clip-path e mask-image per rivelazioni geometriche personalizzate e dissolvenze fluide.
  • Utilizza le animazioni basate sullo scorrimento (animation-timeline: scroll()) per gli effetti di scorrimento non essenziali anziché i listener JS.
  • Utilizza le transizioni di visualizzazione per animare senza problemi gli stati di layout complessi.

Prestazioni

  • Preferisci animare opacity e transform (incluse le singole proprietà di trasformazione, ad esempio translate anziché left/right/top/bottom) per assicurarti che le animazioni rimangano nel thread del compositore.
  • Utilizza transition-behavior: allow-discrete, @starting-style e (per gli elementi di livello superiore come <dialog> o [popover]) overlay per animare in modo nativo gli stati di entrata e uscita discreti; consulta Animare l'entrata e l'uscita degli elementi e Animare gli elementi da e verso il livello superiore.
.popover-reveal {
  /* Transition discrete display and top-layer overlay alongside opacity */
  transition:
    opacity ease-out,
    display,
    overlay;
  transition-duration: 0.2s;
  transition-behavior: allow-discrete;
}

Accessibilità

Utilizza le query supporti prefers-reduced-motion per disattivare i movimenti intensi per gli utenti che lo preferiscono.

  • Fornisci un meccanismo di pausa: consenti agli utenti di interrompere la riproduzione automatica di caroselli, banner o altre animazioni persistenti.
  • Imposta come predefinite le visualizzazioni statiche: valuta la possibilità di impostare come predefiniti gli stati statici e di consentire agli utenti di attivare il movimento.
  • Non superare i limiti di lampeggiamento (tre al secondo): non includere mai un lampeggiamento rapido da chiaro a scuro. Questi effetti possono causare convulsioni.

NON applicare animation-duration: 0.01ms; a livello globale, in quanto alcune animazioni potrebbero diventare più stridenti. Gestisci invece le preferenze di movimento caso per caso utilizzando uno dei due approcci, nidificando la query @media all'interno della regola in modo che entrambe le versioni vengano mantenute insieme:

  • Inizia con le animazioni attivate per impostazione predefinita e riducile utilizzando @media (prefers-reduced-motion: reduce):
/* Good: Dampen spin states or slow down animations for reduced motion */
.spinner {
  animation: spin 1s linear infinite;
  @media (prefers-reduced-motion: reduce) {
    animation: none;
    opacity: 0.5;
  }
}

progress:not([value]) {
  animation: slide 1s infinite linear;
  @media (prefers-reduced-motion: reduce) {
    animation-duration: 20s;
  }
}
  • Inizia senza animazioni per impostazione predefinita e attivale utilizzando @media (prefers-reduced-motion: no-preference):
/* Good: Default to static or gentle states and opt-in to full motion */
.spinner {
  opacity: 0.5;
  @media (prefers-reduced-motion: no-preference) {
    opacity: 1;
    animation: spin 1s linear infinite;
  }
}

progress:not([value]) {
  animation: slide 20s infinite linear;
  @media (prefers-reduced-motion: no-preference) {
    animation-duration: 1s;
  }
}

Utilizzo degli eventi di animazione e transizione in JavaScript

Sia nell'interfaccia AnimationEvent (animationstart, animationiteration, animationend, animationcancel) che nell'interfaccia TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel), event.target fornisce l'elemento DOM di origine. Per accedere all'oggetto Animation specifico (Animation, CSSAnimation o CSSTransition) che ha attivato l'evento, utilizza la proprietà di sola lettura event.animation.

Quando event.animation non è disponibile, in genere puoi trovare l'istanza Animation chiamando event.target.getAnimations() e filtrando per event.animationName (o event.propertyName per le transizioni). Tuttavia, è preferibile utilizzare direttamente event.animation per evitare diversi scenari in cui questo fallback che filtra event.target.getAnimations() non funziona come previsto:

  • Le animazioni dell'API Web Animations (element.animate()) non hanno un animationName, quindi non possono essere abbinate per nome quando si filtra event.target.getAnimations().
  • Il filtro per animationName per AnimationEvent è ambiguo quando più animazioni con lo stesso nome sono associate allo stesso elemento.
    • Tieni presente che il filtro su transitionProperty per TransitionEvent funziona perché esiste un solo evento per proprietà di transizione.
  • event.target punta sempre all'elemento DOM di origine, quindi non distingue tra le animazioni in esecuzione sull'elemento stesso e quelle su uno dei suoi pseudo-elementi, mentre event.animation.effect.pseudoElement espone direttamente lo pseudo-elemento di destinazione.
// Works in most basic CSS animation cases, but fails for WAAPI animations,
// duplicate animation names on the same element, or pseudo-elements.
document.addEventListener('animationstart', (event) => {
  const animation = event.target
    .getAnimations()
    .find((anim) => anim.animationName === event.animationName);
  console.log(animation);
});

// PREFERRED: Directly access the exact Animation object via event.animation
document.addEventListener('animationstart', (event) => {
  const animation = event.animation;
  console.log(animation);
});

Puoi rilevare il supporto delle funzionalità utilizzando 'animation' in AnimationEvent.prototype (o 'animation' in TransitionEvent.prototype).