Web Animations API

L’avvento del CSS3 ha aggiunto la possibilità creare animazioni tramite foglio di stile. Il tutto avviene tramite le parole chiave animation e keyframes. Per capire il funzionamento delle animazioni CSS vediamo questo semplice esempio.

.square {
    width: 100px;
    height: 100px;
    background-color: red;
}

.square.animated {
    animation: backgroud-color-animation 1s;
}

@keyframes backgroud-color-animation {
    from {background-color: red;}
    to {background-color: yellow;}
}

Come vedete tramite la proprietà animation definiamo il nome dell’animazione e la sua durata. Tramite invece keyframes definiamo quali sono gli stati di cui l’animazione stessa è composta. In pratica stiamo dicendo che gli elementi marcati con la classe animated e square passeranno dall’avere un colore di background rosso ad uno giallo in un secondo.

Esempio di animazione CSS

Esempio di animazione CSS

A volte abbiamo bisogno di pilotare l’avvio di un’animazione, ad esempio dopo che l’utente clicca su un determinato button. Immaginate ad esempio l’apertura di un menù laterale. In questi casi basta collegare la classe animated proprio in risposta agli eventi scatenati dall’utente.

Web Animations Api

Questo approccio ha però dei limiti, immaginate ad esempio di voler scatenare una funzione JavaScript al termine dell’animazione. Questa cosa non è facilmente fattibile a meno di hack clamorosi come appoggiarsi a setTimeout vari ed eventuali. In nostro soccorso ci sono le Web Animations Api, nuova specifica W3C che permette di controllare le nostre animazioni CSS con del semplice codice JavaScript. Come vedremo grazie a questa Api potremmo controllare il flusso di un’animazione e, soprattutto, combinarle in gruppi o sequenze. Il tutto senza perdere l’espressività semantica data dal CSS3.

Can I use it?

Sbirciando sulla documentazione di MDN vediamo che questa Api è sperimentale, perciò non è supportata ancora da tutti i browser. Interrogando il sito caniuse.com possiamo vedere che è implementata esclusivamente da Chrome, Opera e Firefox. Per fortuna è disponibile un polyfill di cui potete recuperare il codice in questo repository GitHub.

Compatibilità Web Animations API

Compatibilità Web Animations API

Let’s code

Partiamo con un primo esempio: una semplice animazione che muove un’immagine da sinistra verso destra tramite la proprietà left. Il markup della nostra pagina è il seguente:

<html>
<body>
<style>
    .logo {
        position: fixed;
        width: 50px;
        height: 50px;
        background: url(...);
    }
</style>
<div class="logo" style="top: 100px;left: 0px;"></div>
<button onclick="play()">Play</button>
<button onclick="pause()">Pause</button>
<button onclick="reverse()">Reverse</button>
<button onclick="faster()">faster</button>
<button onclick="slower()">slower</button>
<input id="range" type="range" value="0" min="0" max="100">
<span id="label"></span>
</body>
</html>

Abbiamo quindi un div posizionato in maniera fissa e una button-bar che ci servirà per pilotare la nostra animazione. Lo script che utilizzeremo è il seguente:

var element = document.querySelector('.logo');

var keyframes = [
    {
        left: "0px"
    },
    {
        left: "500px"
    }
];

var timing = {
    duration: 2000,
    fill:"forwards",
    easing: 'ease-in-out'
};

var effect = new KeyframeEffect(element, keyframes, timing);

var animation = new Animation(effect, document.timeline);

animation.onfinish = function () {
    document.querySelector('#label').textContent = 'Animation Finished on ' + new Date();
};

function play() {
    animation.play();
}

function pause() {
    animation.pause();
}

function reverse() {
    animation.reverse();
}

function faster() {
    animation.playbackRate *= 2;
}

function slower() {
    animation.playbackRate /= 2;
}

var range = document.querySelector('#range');

range.addEventListener("input", function() {
    if (animation.playState === 'paused') {
        animation.currentTime = 2000 * range.value / 100;
    }
}, false);

Per creare un’animazione dobbiamo innanzitutto creare un oggetto KeyframeEffect a cui passiamo l’elemento del DOM a cui vogliamo applicare l’animazione i keyframes ed il timing. Usiamo poi questo oggetto per creare un Animation e collegarla alla timeline della pagina. Notate che per definire l’animazione abbiamo utilizzato per le stesse proprietà che avremmo utilizzato per una normale animazione CSS. Una volta ottenuta l’animazione possiamo:

  • Controllare l’animazione tramite i metodi play, pause e reverse
  • Possiamo invocare una callback alla fine dell’animazione tramite onfinish
  • Modificare la velocità (ed il verso) dell’animazione tramite la proprietà playbackRate
  • Posizionare l’animazione in un momento preciso nel tempo tramite la proprietà currentTime

Potete provare questa animazione a questa pagina demo.

Web Animations Api

Web Animations Api

Gruppi e Sequenze

Come accennato all’inizio del post tramite le Web Animations Api possiamo anche animare gruppi di oggetti o creare una sequenza di animazioni. Vediamo subito il prossimo esempio sulla creazione dei gruppi. Per i prossimi esempi il markup sarà identico, avremo semplicemente più div da muovere.

var firstLogo = document.querySelector('#firstLogo');
var secondLogo = document.querySelector('#secondLogo');

var firstKeyframes = [
    {
        left: "0px"
    },
    {
        left: "500px"
    }
];

var secondKeyframes = [
    {
        right: "0px"
    },
    {
        right: "500px"
    }
];

var firstTiming = {
    duration: 2000,
    fill:"forwards",
    easing: 'ease-in-out'
};

var secondTiming = {
    duration: 4000,
    fill:"forwards",
    easing: 'ease-in-out'
};

var effects = [
    new KeyframeEffect(firstLogo, firstKeyframes, firstTiming),
    new KeyframeEffect(secondLogo, secondKeyframes, secondTiming)
];

var group = new GroupEffect(effects);

var animation = new Animation(group,document.timeline);

//Controllo animazione

In questo caso sfruttiamo l’oggetto GroupEffect per wrappare un array di KeyframeEffect. Il risultato sarà una serie di oggetti che si muovono contemporaneamente. Potete giocare con queste animazioni in questa demo. Vediamo come, in maniera del tutto similare, possiamo invece creare una sequenza.

var elements = document.querySelectorAll('.logo');
var effects = [];

var keyframes = [
    {
        left: "0px"
    },
    {
        left: "500px"
    }
];

var timing = {
    duration: 2000,
    fill:"forwards",
    easing: 'ease-in-out'
};

elements.forEach(function(element){
    effects.push(new KeyframeEffect(element, keyframes, timing));
});

var sequence = new SequenceEffect(effects);

var animation = new Animation(sequence,document.timeline);

//Controllo animazione

Il codice è praticamente identico se non fosse per l’utilizzo dell’oggetto SequenceEffect. Come per i precedenti esempi avete a disposizione una pagina demo.

Le due tecniche si posso ovviamente mescolare tra loro per ottenere delle animazioni davvero complesse come quella visibile in questo ultimo esempio. Provate a cliccare su “Full Screen” e notate come gli elementi scompaiono dallo schermo in sequenza.

Gestione di layout complessi con Web Animations Api

Gestione di layout complessi con Web Animations Api

Conclusioni

Qualcuno di voi potrebbe dire che lo stesso risultato lo si può ottenere con jQuery.animate, ma in realtà la differenza è sostanziale. Nei browser dove questa Api è supportata nativamente ha performance quasi del tutto identiche a quelle applicate tramite foglio di stile, per via del fatto che il browser sfrutta lo stesso engine delle animazioni CSS. Anche negli altri casi però le performance sono molto alte, come potete verificare voi stessi grazie a questo sito creato da Mozilla. Per chi è curioso il codice di tutti gli esempi è disponibile in questo repository GitHub. Alla prossima.

Francesco Strazzullo

Faccio il Front-end developer per e-xtrategy dove mi occupo di applicazioni AngularJS e mobile. In passato ho lavorato principalmente con applicazioni con stack Spring+Hibernate+JSF 2.X+Primefaces. Sono tra i collaboratori del progetto Primefaces Extensions: suite di componenti aggiuntivi ufficialmente riconosciuta da Primefaces. Sono anche uno dei fondatori del progetto MaterialPrime: una libreria JSF che segue le direttive del Material Design di Google.