Le animazioni CSS ci consentono di realizzare semplici animazioni senza lâutilizzo di JavaScript.
JavaScript può eventualmente essere utilizzato per controllare le animazioni CSS e migliorarle, aggiungendo qualche riga di codice.
Transizioni CSS
Lâidea che sta alla base delle transizioni CSS è molto semplice. Ci consente di definire come animare i cambiamenti su una determinata proprietà . Quando il valore di questa proprietà cambierà , il browser mostrerà lâanimazione.
Questo è tutto, lâunica cosa di cui abbiamo bisogno ora è di cambiare il valore della proprietà , e la transizione definita verrà eseguita dal browser.
Ad esempio, il codice CSS qui sotto definisce unâanimazione al cambiamento della proprietà background-color con durata 3 secondi:
.animated {
transition-property: background-color;
transition-duration: 3s;
}
Quindi ora, se un elemento possiede la class .animated, qualsiasi cambiamento al suo background-color verrà animato con una durata di 3 secondi.
Provate a cliccare il bottone qui sotto per vedere lâanimazione sul background:
<button id="color">Click me</button>
<style>
#color {
transition-property: background-color;
transition-duration: 3s;
}
</style>
<script>
color.onclick = function() {
this.style.backgroundColor = 'red';
};
</script>
Abbiamo a disposizione 4 proprietà per descrivere le transizioni CSS:
transition-propertytransition-durationtransition-timing-functiontransition-delay
Le studieremo in dettaglio a breve, per ora notiamo che la proprietà comune transition ci consente di dichiararle tutte insieme nellâordine: property duration timing-function delay, e ci consente anche di definire più proprietà da animare.
Ad esempio, nel bottone qui sotto abbiamo definito unâanimazione sia su color che su font-size:
<button id="growing">Click me</button>
<style>
#growing {
transition: font-size 3s, color 2s;
}
</style>
<script>
growing.onclick = function() {
this.style.fontSize = '36px';
this.style.color = 'red';
};
</script>
Ora, vediamo le proprietà dellâanimazione nel dettaglio.
transition-property
In transition-property, elenchiamo una lista di proprietà da animare, ad esempio: left, margin-left, height, color. Oppure possiamo scrivere all, che significa âanima tutte le proprietà â.
Notiamo che ci sono proprietà che non possono essere animate. Anche se la maggior parte delle proprietà utilizzate sono animabili.
transition-duration
In transition-duration possiamo definire la durata dellâanimazione. Il tempo deve essere nel formato temporale CSS: in secondi s o millisecondi ms.
transition-delay
In transition-delay possiamo specificare un ritardo dellâanimazione. Ad esempio, se transition-delay è impostato ad 1s e transition-duration vale 2s, allora lâanimazione inizierà con 1 secondo di ritardo rispetto al cambiamento del valore della proprietà a cui fa riferimento, e avrà una durata totale di 2 secondi.
Eâ possibile definire anche valori negativi. In questo caso lâanimazione inizierà immediatamente, ma il punto di inizio verrà spostato di tanti secondi quanti sono quelli del tempo di ritardo fornito. Ad esempio, se transition-delay è impostato a -1s e transition-duration vale 2s, lâanimazione inizierà a metà ed avrà una durata totale di 1 secondo.
Qui vediamo un animazione che scorre le cifre da 0 a 9 utilizzando la proprietà CSS translate:
stripe.onclick = function() {
stripe.classList.add('animate');
};#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: linear;
}<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script src="script.js"></script>
</body>
</html>La proprietà transform viene animata in questo modo:
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
}
Nellâesempio visto sopra, JavaScript aggiunge la classe .animate allâelemento, iniziando così lâanimazione:
stripe.classList.add('animate');
Possiamo anche iniziarla a metà della transizione, da un numero specifico, ad esempio corrispondente al secondo attuale, utilizzando un valore negativo per la proprietà transition-delay.
Nellâesempio sotto, se cliccate il numero, lâanimazione inizierà dal secondo attuale:
stripe.onclick = function() {
let sec = new Date().getSeconds() % 10;
stripe.style.transitionDelay = '-' + sec + 's';
stripe.classList.add('animate');
};#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: linear;
}<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script src="script.js"></script>
</body>
</html>Con JavaScript possiamo fare la stessa cosa, ma con qualche riga di codice in più:
stripe.onclick = function() {
let sec = new Date().getSeconds() % 10;
// ad esempio, -3s in questo caso, farà iniziare l'animazione al terzo secondo
stripe.style.transitionDelay = '-' + sec + 's';
stripe.classList.add('animate');
};
transition-timing-function
La funzione di temporizzazione permette di descrivere la distribuzione dellâanimazione lungo la sua durata. Potrà iniziare più lenta e poi accelerare, o vice versa.
A prima vista sembra essere una delle proprietà più complesse. Ma diventa piuttosto semplice se ci dedichiamo un poâ di tempo per capirla.
Questa proprietà accetta due tipi di valore: una curva di Bezier oppure degli step. Iniziamo a vedere il caso in cui si utilizza la curva di Bezier, poiché è il più comune.
Bezier curve
La funziona di temporizzazione può essere definita utilizzando una curva di Bezier con 4 punti di controllo che soddisfino le seguenti condizioni:
- Il primo punto di controllo deve essere:
(0,0). - Lâultimo punto di controllo deve essere:
(1,1). - Per i valori intermedi, il valore di
xdeve essere compreso nellâintervallo0..1, mentreypuò assumere qualsiasi valore.
La sintassi per descrivere una curva di Bezier nel CSS: cubic-bezier(x2, y2, x3, y3). In questo caso è necessario specificare solamente il terzo ed il quarto punto di controllo, poiché il primo viene impostasto a (0,0) ed il quarto a (1,1).
La funzione di temporizzazione descrive la velocità di riproduzione dellâanimazione.
- Lâasse delle
xrappresenta il tempo:0â lâinizio,1il termine dellâanimazione, specificato intransition-duration. - Lâasse delle
yspecifica il completamento del processo:0il valore iniziale della proprietà ,1il valore finale.
La variante più semplice è quando unâanimazione procede uniformemente, con velocita lineare. Questo tipo di animazione può essere definito con la curva cubic-bezier(0, 0, 1, 1).
Così è come appare la curva descritta:
â¦Come possiamo vedere, è una semplice linea retta. Al passare del tempo (x), il completamento (y) dellâanimazione passa da 0 a 1.
Il treno nellâesempio sotto va da sinistra verso destra con velocità costante (provate a cliccarci sopra):
.train {
position: relative;
cursor: pointer;
width: 177px;
height: 160px;
left: 0;
transition: left 5s cubic-bezier(0, 0, 1, 1);
}<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<img class="train" src="https://js.cx/clipart/train.gif" onclick="this.style.left='450px'">
</body>
</html>La transition CSS è basata su questa curva:
.train {
left: 0;
transition: left 5s cubic-bezier(0, 0, 1, 1);
/* JavaScript imposta left a 450px */
}
â¦Come potremmo mostrare il treno che rallenta?
Possiamo utilizzare una curva di Bezier differente: cubic-bezier(0.0, 0.5, 0.5 ,1.0).
La curva appare così:
Come possiamo vedere, il processo inizia velocemente: la curva cresce verso lâalto, e successivamente rallenta.
Qui vediamo la funzione di temporizzazione in azione (cliccate il treno):
.train {
position: relative;
cursor: pointer;
width: 177px;
height: 160px;
left: 0px;
transition: left 5s cubic-bezier(0.0, 0.5, 0.5, 1.0);
}<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<img class="train" src="https://js.cx/clipart/train.gif" onclick="this.style.left='450px'">
</body>
</html>CSS:
.train {
left: 0;
transition: left 5s cubic-bezier(0, .5, .5, 1);
/* JavaScript imposta left a 450px */
}
Abbiamo a disposizione anche alcune curve integrate: linear, ease, ease-in, ease-out e ease-in-out.
La curva linear è un abbreviazione per cubic-bezier(0, 0, 1, 1), una linea retta, che abbiamo descritto sopra.
Gli altri nomi sono delle abbreviazioni per le seguenti curve cubic-bezier:
ease* |
ease-in |
ease-out |
ease-in-out |
|---|---|---|---|
(0.25, 0.1, 0.25, 1.0) |
(0.42, 0, 1.0, 1.0) |
(0, 0, 0.58, 1.0) |
(0.42, 0, 0.58, 1.0) |
* â di default, se non viene fornita nessuna funzione di temporizzazione, verrà utilizzata ease.
Potremmo quindi utilizzare ease-out per rallentare lâanimazione del treno:
.train {
left: 0;
transition: left 5s ease-out;
/* same as transition: left 5s cubic-bezier(0, .5, .5, 1); */
}
Anche se apparirà leggermente diversa.
Una curva di Bezier può far eccedere lâanimazione dal suo intervallo.
I punti di controllo nella curva, sono liberi di definire un valore qualsiasi per le coordindate y: sia valori grandi che negativi. Questo si rifletterà sulla curva di Bezier che potrà essere molto alta o molto bassa, facendo eccedere lâanimazione dal suo intervallo normale.
Nellâesempio sotto il codice dellâanimazione è:
.train {
left: 100px;
transition: left 5s cubic-bezier(.5, -1, .5, 2);
/* JavaScript imposta left a 400px */
}
La proprietà left sarà animata da 100px a 400px.
Ma se provate a cliccare il treno, vedrete che:
- Primo, il treno va indietro:
leftdiventa inferiore a100px. - Poi va in avanti, leggermente oltre i
400px. - E successivamente torna nuovamente indietro, fino a
400px.
.train {
position: relative;
cursor: pointer;
width: 177px;
height: 160px;
left: 100px;
transition: left 5s cubic-bezier(.5, -1, .5, 2);
}<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<img class="train" src="https://js.cx/clipart/train.gif" onclick="this.style.left='400px'">
</body>
</html>Ciò che accadde è abbastanza ovvio, se andiamo a guardare il grafico della curva di Bezier:
Abbiamo impostato la coordinata y del secondo punto, sotto lo zero, ed il terzo punto va oltre 1, quindi la curva esce dal quadrante âstandardâ. La y eccede quindi lâintervallo âstandardâ 0..1.
Come sappiamo, y misura il âgrado di completezza dellâanimazioneâ. Il valore y = 0 corrisponde al valore iniziale della proprietà , mentre y = 1 corrisponde al valore finale. Quindi i valori di y<0 spostando la proprietà sotto il valore iniziale di left e y>1 oltre il valore finale di left.
Questa ovviamente è una variante più âleggeraâ. Se impostiamo y a valori come -99 e 99 allora il treno eccederebbe lâintervallo più evidentemente.
Ma come possiamo definire una curva di Bezier per una specifica animazione? Esistono diversi strumenti che ci permettono di farlo. Ad esempio, possiamo utilizzare http://cubic-bezier.com/.
Steps
La funzione di temporizzazione steps(number of steps[, start/end]) consente di definire unâanimazione in steps (âpassiâ).
Vediamolo in azione su un esempio con cifre.
Qui abbiamo unâelenco di cifre, senza alcuna animazione:
#digit {
border: 1px solid red;
width: 1.2em;
}
#stripe {
display: inline-block;
font: 32px monospace;
}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="digit"><div id="stripe">0123456789</div></div>
</body>
</html>Ciò che faremo sarà far apparire le cifre in modo discreto facendo si che la lista esterna al box rosso sia invisibile, e faremo scorrere la lista a sinistra ad ogni step.
Dovremo definire 9 step, uno per ogni cifra:
#stripe.animate {
transform: translate(-90%);
transition: transform 9s steps(9, start);
}
In azione:
#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: steps(9, start);
}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script>
digit.onclick = function() {
stripe.classList.add('animate');
}
</script>
</body>
</html>Il primo argomento di steps(9, start) è il numero di step. La transform verrà divisa in 9 parti (10% ad ognuna). Anche lâintervallo di tempo sarà diviso automaticamente in 9 parti, quindi transition: 9s farà durare lâanimazione 9 secondi, 1 secondo per ogni cifra.
Il secondo argomento è una delle due parole chiave: start o end.
La parola chiave start significa che il primo step verrà eseguito immediatamente allâinizio dellâanimazione.
Possiamo osservarlo durante lâanimazione: quando clicchiamo su una cifra cambia ad 1 (il primo step) immediatamente, e successivamente cambierà al termine del secondo successivo.
Il processo progredirà in questo modo:
0sâ-10%(il primo cambiamento allâinizio del primo secondo, immediatamente)1sâ-20%- â¦
8sâ-80%- (lâultimo secondo mostra il valore finale).
In alternativa il valore end starà a significare che il cambiamento dovrebbe essere applicato non dallâinizio, ma alla fine di ogni secondo.
Il processo progredirà in questo modo:
0sâ01sâ-10%(il primo cambiamento alla fine del primo secondo)2sâ-20%- â¦
9sâ-90%
Qui vediamo steps(9, end) in azione (da notare la pausa tra il cambiamento della prima cifra):
#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: steps(9, end);
}<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script>
digit.onclick = function() {
stripe.classList.add('animate');
}
</script>
</body>
</html>Anche in questo caso esistono delle abbreviazioni:
step-startequivale asteps(1, start). Ovvero, lâanimazione inizierà immediatamente e compierà il primo step. Quindi inizierà e terminerà immediatamente, come se non ci fosse alcuna animazione.step-endequivale asteps(1, end): compie lâanimazione in un solo step al termine ditransition-duration.
Questi valori vengono utilizzati raramente. Poiché non definiscono delle vere animazioni, ma piuttosto un cambiamento di un solo step.
Lâevento transitionend
Al termine dellâanimazione CSS, verrà innescato lâevento transitionend.
Viene spesso utilizzato per compiere unâazione al termine dellâanimazione. In questo modo possiamo unire le animazioni.
Ad esempio, la nave che vediamo nellâesempio sotto inizia a navigare quando cliccata, e poi torna indietro, ogni volta si allontana sempre di più verso destra:
Lâanimazione viene avviata dalla funzione go che viene rieseguita al termine di ogni transizione, invertendo la direzione:
boat.onclick = function() {
//...
let times = 1;
function go() {
if (times % 2) {
// naviga verso destra
boat.classList.remove('back');
boat.style.marginLeft = 100 * times + 200 + 'px';
} else {
// naviga verso sinistra
boat.classList.add('back');
boat.style.marginLeft = 100 * times - 200 + 'px';
}
}
go();
boat.addEventListener('transitionend', function() {
times++;
go();
});
};
Lâoggetto emesso dallâevento transitionend possiede un paio di proprietà specifiche:
event.propertyName- La proprietà che ha concluso lâanimazione. Può essere utile se abbiamo definito animazioni per più proprietà .
event.elapsedTime- Il tempo (in secondi) impiegato per concludere lâanimazione, senza considerare
transition-delay.
Keyframes
Possiamo unire tra di loro più animazioni utilizzando la regola CSS @keyframes.
Tale regola specifica il ânomeâ dellâanimazione e le modalità : cosa, quando e dove eseguire lâanimazione. Successivamente, utilizzando la proprietà animation, possiamo attribuire lâanimazione allâelemento ed eventualmente specificare parametri addizionali.
Qui vediamo unâesempio commentato:
<div class="progress"></div>
<style>
@keyframes go-left-right { /* gli diamo un nome: "go-left-right" */
from { left: 0px; } /* anima da sinistra: 0px */
to { left: calc(100% - 50px); } /* anima verso sinistra: 100%-50px */
}
.progress {
animation: go-left-right 3s infinite alternate;
/* applichiamo l'animazione "go-left-right" all'elemento
durata 3 secondi
ripetizioni: infinite
alterna le direzione ad ogni iterazione
*/
position: relative;
border: 2px solid green;
width: 50px;
height: 20px;
background: lime;
}
</style>
Potete trovare molti articoli sul tema @keyframes e la specifica dettagliata.
Probabilmente non avrete bisogno di utilizzare @keyframes spesso, a meno che tutto nel vostro sito sia in costante movimento.
Riepilogo
Le animazioni CSS consentono di definire delle animazioni fluide (o meno) su una o più proprietà CSS.
Sono utili nella maggior parte dei casi in cui dobbiamo definire delle animazioni. Possiamo anche utilizzare JavaScript per definire le animazioni, ed il prossimo capitolo sarà infatti dedicato a questo.
Le limitazioni delle animazioni CSS rispetto a quelle definite utilizzando JavaScript:
- Le cose semplici sono facili da realizzare.
- Più veloci e leggere per la CPU.
- Le animazioni JavaScript sono più flessibili. Possono implementare qualsiasi logica, come un âesplosioneâ di un elemento.
- Non si limitano a cambiamenti di proprietà . Con JavaScript possiamo anche creare nuovi elementi da aggiungere allâanimazione.
La maggior parte delle animazioni possono essere implementate con CSS come descritto in questo articolo. Insieme allâevento transitionend possiamo eseguire codice al termine dellâanimazione, integrandoci perfettamente con lâanimazione.
Nel prossimo articolo vedremo le animazioni con JavaScript, andando a trattare casi più complessi.
Commenti
<code>, per molte righe â includile nel tag<pre>, per più di 10 righe â utilizza una sandbox (plnkr, jsbin, codepenâ¦)