Niveau : Expert

Timing des animations et des transitions en CSS3

Tutoriel par (Développeur Front-end, Montréal, Québec)
Créé le , mis à jour le (82822 lectures)
Tags : css, animation, css3, transition, delay, steps, bezier, cubic

L'arrivée de CSS3 il y a quelques années a pour plusieurs d'entre nous grandement révolutionné la manière dont nous intégrions un site Web. D'abord, ce furent les propriétés purement graphiques (coins arrondis, ombres portées) qui frayèrent leur chemin jusqu'en mode production. Ces propriétés étaient les mieux supportées des différents navigateurs, et elles étaient facilement imitables sur les plus anciens grâce aux outils à notre disposition (Par exemple: Css3Pie, Selectivizr, Modernizr, etc). Même la tâche redondante consistant à préfixer nos propriétés CSS3 est aujourd'hui facilitée avec Prefixr et PrefixFree.

Aujourd'hui, l'étendue des possibilités auxquelles a accès un intégrateur est assez impressionnante. Et enfin, les propriétés de transition et d'animation sont plutôt bien supportées par l'ensemble des navigateurs modernes, y compris à partir d'Internet Explorer 10. Évidemment, le manque de support les anciennes versions d'Internet Explorer est le plus grand handicap de ces nouvelles propriétés. Par contre, ces modules CSS3 sont déjà largement utilisable en production dans le domaine du Web mobile !

[![Texte alternatif:!:]!]

Le sujet des transitions et des animations a déjà été traité par maints auteurs. Cependant, ces deux modules CSS3 comportent plusieurs particularités plus difficiles à saisir; ou à tout le moins, peu connues. Le but de ce tutoriel sera de faire le point sur le fonctionnement des effets de timing des transitions et des animations. En même temps, je m'attarderai à vous faire découvrir deux fonctions CSS3 assez peu connues appelées: cubic-bezier, qui vous permettra de créer vos propres courbes de bézier gérant la fluidité de vos animations, et steps, qui vous permettra de séparer votre transition en plusieurs étapes franches (en "marche d'escalier").

Avant de commencer, veuillez noter que le terme de transition s'appliquera aussi bien au module transition CSS3 qu'aux effets de timing des animations. Cela est dû au fait qu'une transition aura lieu entre chaque état d'une animation; rendant cette dernière plus fluide.

Survol de l'utilisation des transitions et des animations

Avant toute chose, débutons par un survol de l'utilisation des transitions et des animations. La compréhension du fonctionnement de ces deux modules est évidemment pré-requise à la compréhension de ce tutoriel.

Quelques Références

Transitions:
Animations:

Transitions

Les transitions permettent de rendre fluide le changement de valeur d'une propriété CSS. Ce changement d'état peut-être déclenché à l'ajout d'une pseudo-classe (:hover, :focus, :active, :target, :checked, etc) ou à la modification d'une ou plusieurs class (ajout/suppression via Javascript).

Les propriétés CSS pouvant subir une transition sont à priori toutes les valeurs numériques. Par exemple: width, margin, padding, color, etc. Une liste (incomplète) de ces propriétés est disponible sur le site du w3c.

Les différentes propriétés de transitions sont:

  • transition-property : Définit les propriétés CSS qui subiront une transition (ex: width). Le mot clé all ciblera toutes les propriétés capable de subir une transition. (Obligatoire)
  • transition-duration : Définit la durée en temps de la transition en seconde (s) ou milliseconde (ms). (Obligatoire)
  • transition-timing-function : Définit un effet de timing à utiliser (le modèle d'interpolation). Nous reviendrons à ces effets dans un autre chapitre. (Facultative)
  • transition-delay : Définit l'avance ou le retard que prendra la transition vis à vis de son déclenchement. (Facultative)

Par exemple, le code suivant ciblera toutes les propriétés CSS du sélecteur. La transition aura une durée de 500 millisecondes, commencera lentement et finira plus rapidement (ease-in) et elle commencera une seconde après le déclenchement du changement d'état du sélecteur.

selecteur {
  transition-property: all;
  transition-duration: .5s; /* on aurait pu écrire 500ms également */
  transition-timing-function: ease-in;
  transition-delay: 1s;
}

Remarque: Il est souvent préférable de déclarer la transition sur le sélecteur seul et pas sur un sélecteur avec une pseudo-classe (comme selecteur:hover) puisque dans ce dernier cas de figure, la transition n'aura lieu qu'au moment où la souris survole l'élément, et pas lorsque celle-ci le quittera.

Évidemment, les transitions CSS ont également une syntaxe simplifiée (incroyablement utile avec tous les préfixes navigateurs). Ainsi, on écrira:

selecteur {
  transition: <transition-property> <transition-duration> <transition-timing-function> <transition-delay>;
}

/* Ou en cumulant plusieurs propriétés */
selecteur {
  transition: <transition-property> <transition-duration> <transition-timing-function> <transition-delay>,
  <transition-property> <transition-duration> <transition-timing-function> <transition-delay>;
}

Remarque : Les transitions sont encore à l'état d'ébauche au W3C, les préfixes navigateurs seront donc encore de mise. Les navigateurs Gecko comme Firefox (-moz-), les navigateurs Webkit comme Safari, Google Chrome et la majorité des navigateurs mobiles (-webkit-), et les navigateurs Presto comme Opera (-o-) supportent les transitions avec préfixe.

Animations

Les animations CSS3 sont semblables aux transitions. La majeure différence entre les deux est que les animations permettront un contrôle très précis dans le temps de la valeur que prendront les différentes propriétés CSS.

Une animation peut être lancée lors d'un changement de pseudo-classe (:hover, :focus, :active, :target, :checked, etc), lors d'un changement de class via Javascript, ou simplement lors du chargement de la page.

Si l'animation n'est répétée qu'une fois par défaut, elle peut être itérée à souhait.

Pour réaliser une animation CSS, nous avons besoin de deux éléments distincts: une déclaration d'images-clés (keyframe) définie au sein d'une @-rule, et un lien d'un sélecteur vers cette animation au sein d'un bloc de code CSS.

Très rapidement, voilà de quoi pourrait avoir l'air une animation:

@keyframes monanimation {
  0% {
    color: blue;
  }
  100%{
    color: papayawhip;
  }
}
        
selecteur:hover,
selecteur:focus,
selecteur:active{
  animation: monanimation 1.5s ease-in-out;
}

Les animations étant encore à l'état d'ébauche, il vous faudra évidemment ajouter les préfixes navigateurs (présentement, Webkit -webkit-, Gecko -moz- et Opera -o- supportent les animations. Il faudra également pensé à IE 10 -ms-).

Ainsi, nous devrons écrire @-moz-keyframes et -moz-animation afin que nos animations soient comprises sur Firefox. Le même principe s'appliquera aux autres navigateurs.

Les Keyframes

Les keyframes représentent le déroulement de votre animation. Les valeurs en pourcentage représentent un moment temporel précis au cours de la durée de votre animation. Ainsi, 10% représentera la 6ème seconde d'une animation durant une minute.

Remarque : Il est à noter que les keyframes 0% et 100% sont obligatoires et que leur omission empêchera le fonctionnement de votre animation sur certains navigateurs.

Voici quelques remarques en vrac :

  • Une transition est effectuée entre chaque keyframe rendant l'animation plus fluide.
  • Les keyframes n'ont pas besoin d'être en ordre, le keyframe 100% peut très bien être déclaré avant le keyframe 0%.
  • Si plusieurs keyframes ont la même valeur, il est possible de les insérer sur la même ligne en les séparant d'une virgule.

Par exemple, le code suivant serait tout à fait valide :

@-webkit-keyframes bouncing {
40%, 70%, 90%{
  bottom: 0;
  -webkit-animation-timing-function: ease-out;
}
0% {
  bottom: 200px;
  left: 0;
  -webkit-animation-timing-function: ease-in;
}
55% {
  bottom: 50px;
  -webkit-animation-timing-function: ease-in;
}
80% {
  bottom: 25px;
  -webkit-animation-timing-function: ease-in;
}
95% {
  bottom: 10px;
  -webkit-animation-timing-function: ease-in;
}
100% {
  left: 110px;
  bottom: 0;
  -webkit-animation-timing-function: ease-out;
}
}

Fait à noter en lien avec le sujet principal de ce tutoriel, la propriété animation-timing-function est la seule propriété d'une animation à pouvoir être insérée au sein d'une keyframe.

Un exemple de l'animation avec l'utilisation d'un effet de timing. Et la même animation, sans effet de timing. Vous comprendrez sûrement l'importance de bien utiliser ces dernières !

Lancer notre animation

Maintenant que nos keyframes sont créés et référencées par le nom qui leur a été donné, il ne nous reste plus qu'à appeler cette animation à partir d'un sélecteur. Les différentes propriétés d'animation sont les suivantes:

  • animation-name: Où on indique le nom de l'animation à utiliser. (obligatoire)
  • animation-duration: Où on indique la durée en milliseconde (ms) ou en seconde (s) de l'animation. (Obligatoire)
  • animation-iteration-count: Le nombre de fois que l'animation doit être effectué. La valeur devra être un nombre entier ou le mot clé infinite pour que l'animation se répète à l'infinie. La valeur par défaut est de 1. (Facultative)
  • animation-direction: Définira si l'animation doit toujours jouer du début à la fin, ou si une fois rendu à la fin, elle doit refaire l'animation en sens inverse. Par défaut l'animation recommencera du début (normal), mais l'utilisation du mot clé alternate alternera la direction de l'animation. Démonstration d'animation-direction (Facultative)
  • animation-timing-function: Définit un effet de timing à utiliser (le modèle d'interpolation) entre chaque keyframe. Comme vu précédemment, cette propriété est également modifiable au sein même d'une keyframe. (Facultative)
  • animation-delay: Cette valeur définira un délai d'attente avant le début de l'animation, ou dans le cas d'une valeur négative, l'avance que doit prendre l'animation avant de débuter. (Facultative)
  • animation-fill-mode: Cette propriété définit l'état de départ et de fin de votre animation. Voici les différentes valeurs possibles:
    • forwards: indique au navigateur de laisser l'élément dans sont état final lors de la dernière itération. (l'élément ne revient donc pas à son état initial)
    • backwards: indique au navigateur de placer l'élément dans son état définit au keyframe 0% au chargement de la page, même si un délais négatif est indiqué.
    • both: appliquera les deux valeurs précédentes.
    • none: indiquera au navigateur de styler l'élément selon son état à la première keyframe visible (dans le cas d'un délais négatif) et de ramener l'animation à la keyframe 0% après la dernière itération. Ceci est le comportement par défaut.

Évidemment, autant de valeurs (avec les préfixes navigateurs en plus) vous créeront vite 21 lignes supplémentaires dans votre bloc de code. Ainsi, nous utiliserons plus souvent qu'autrement la syntaxe simplifiée:

selecteur {
  animation: <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode>;
}

Ou si vous désirez appliquer plusieurs animations sur un élément:

selecteur {
  animation:  <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode>,
  <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode>;
}
Mettre votre animation sur pause

Et pour clore ce chapitre sur les animations CSS3, il vous est également possible de mettre en pause une animation via la propriété animation-play-state qui prendra l'une des deux valeurs suivantes : running (par défaut) ou paused pour arrêter l'animation.

selecteur:hover {
  -webkit-animation-play-state:paused;
  -moz-animation-play-state:paused;
  animation-play-state:paused;
}

Fonctionnement des fonctions de timing

Comme nous l'avons vu précédemment, autant les transitions que les animations ont recours à des effets de timing. Pour rappel, ces effets sont utilisés en tant que valeur des propriétés suivantes:

  • transition-timing-function
  • animation-timing-function (dans un bloc de code CSS ou au sein d'une keyframe)

Ces effets de timing permettent de gérer la fluidité d'une transition (entre deux propriétés CSS ou entre deux keyframes). Un effet de timing peut-être représenté sous deux formes: une fonction en escalier (stepping function), ou une courbe de Bézier cubique.

Dans le cas de ces deux fonctions, on pourra se les représenter sur un graphique où l'axe des Y représentera chaque étape entre la valeur initiale et la valeur finale de la propriété CSS subissant une transition et l'axe des X représentera l'avancement dans le temps de la transition (de son début à sa fin).

[![Texte alternatif:!:Courbe de Bézier sur un graphique dont l'axe des Y représente la propriété subissant une transition,]!]

Dans le cas du graphique précédent (où une courbe de Bézier est utilisée), notre animation commencera lentement et finira lentement.

De base, les navigateurs fournissent quelques fonctions de transitions par défaut. Celles-ci sont:

  • ease: décélère légèrement au début et termine sa transition lentement. (cubic-bezier(0.25, 0.1, 0.25, 1.0))
  • linear: la transition sera stable sans décélération ni accélération. (cubic-bezier(0.0, 0.0, 1.0, 1.0))
  • ease-in: commencera lentement (cubic-bezier(0.42, 0, 1.0, 1.0))
  • ease-out: finira lentement (cubic-bezier(0, 0, 0.58, 1.0))
  • ease-in-out: commencera et finira lentement (cubic-bezier(0.42, 0, 0.58, 1.0))
  • step-start: L'élément prendra la valeur finale de sa transition dès le déclenchement de la transition. Autrement dit, il n'y aura pas de transition visible. (steps(1, start))
  • step-end: L'élément prendra la valeur finale de sa transition à la fin de la durée prévue par celle-ci. Autrement dit, on aura l'impression qu'un délai a eu lieu. (steps(1, end))

Remarque: Chaque effet par défaut correspond à une fonction cubic-bezier() ou steps(). J'indique les valeurs de chaque fonction à titre de référence, nous nous attarderons plus en détail à l'utilisation plus poussée des fonctions cubic-bezier() et steps() dans les prochains chapitres.

La fonction CSS cubic-bezier()

Sa syntaxe

Une courbe de cubic-bezier sera définie par quatre points (p0, p1, p2, p3) disposés sur un graphique où les axes X et Y auront pour valeur une échelle entre 0 et 1.

Les points p0 et p3 auront toujours pour valeur de positionnement respective (0,0) et (1,1). En soit, le p0 représente le début de la transition et le p3 représente la fin. Ces deux points sont fixes et ne peuvent pas être modifiés.

[![Texte alternatif:!:Graphique représentant les points 0, 1, 2 et 3 positionné sur une échelle de 1 à 0 d'une courbe de b]!]

Les p1 et p2 en revanche pourront être positionnés selon nos besoins sur le graphique à l'aide de la fonction cubic-bezier.

Ainsi, la fonction cubic-bezier nous sert à définir la position en X et en Y des points 1 et 2. La fonction prendra en paramètre 4 nombres flottants représentant respectivement:

transition-timing-function: cubic-bezier(<X p1>, <Y p1>, <X p2>, <Y p2>);

Remarque : Il faut obligatoirement utiliser un nombre flottant. Ainsi, nous devrons écrire 1.0 en lieu et place de 1, et ainsi de suite.

La valeur de position sur l'axe X (représentant le temps) des deux points doit avoir une valeur située entre 0 et 1. La valeur en Y cependant peut prendre n'importe quelle valeur.

Remarque: La possibilité de donner un valeur non-comprise entre 0 et 1 à la valeur Y est une modification récente aux spécifications du w3c. Ce faisant, cette possibilité est présentement ignoré des navigateurs webkit (à ce jour, Google Chrome en est à sa version 15, et Safari à sa version 5.1). Cependant, ce bug devrait être bientôt réglé, et vu la rapidité de propagation des nouvelles versions sur Chrome et Safari, ce n'est qu'une question de mois avant que cette possibilité soit totalement utilisable. Chez Firefox et Opera, tout fonctionne déjà.

L'intérêt premier de pouvoir spécifier n'importe quelle valeur sur l'axe des Y est de permettre très facilement l'utilisation d'effets de bouncing (rebond) dans divers cas.

Exemple de la fonction cubic-bezier() utilisé pour donner un effet de rebond

Si vous avez remarqué, les courbes de Bézier sont proportionnelles au temps et à l'écart entre la valeur de départ et la valeur finale. Ce faisant, plus la transition est longue, et plus l'écart est grand entre l'état initial et final de notre élément, plus l'effet de timing sera visible.

Outils pour générer ses propres courbes de Bézier

Dit ainsi, toute cette théorie semble peut-être compliquer la grande puissance de la fonction cubic-bezier. Mais rassurez-vous, plusieurs outils en ligne vous permettent déjà de générer vos propres courbes de Bézier dans un environnement simple et efficace.

Les fonctionnalités de la majorité de ces outils étant malgré tout limitées (Opera transition-timing-function), je vous évite la longue liste pour ne me concentrer que sur un site: cubic-bezier.com.

Cet outil a été créé par Lea Verou (en anglais) et permet tout simplement de créer vos propres courbes de Bézier applicable aux effets de timing CSS 3. Plusieurs fonctionnalités de cet outil en font un incontournable. Entre autre:

  • Possibilité d'enregistrer et de partager vos propres courbes.
  • Possibilité de tester vos courbes en direct et de les comparer.
  • Interface graphique permettant une compréhension très simple des courbes de Bézier.
  • Un site en HTML5 et canvas très inspirant !

En gros, il ne vous reste plus qu'à le découvrir si ce n'est déjà fait ! (Et si vous ne comprenez pas encore tout à fait la fonction cubic-bézier(), vous y aurez des exemples concrets)

La fonction CSS steps()

La fonction CSS steps() vous permet de définir une transition en "escalier". Cela veut dire que votre transition ne sera pas fluide comme si elle utilisait cubic-bezier, mais passera par des étapes (step) bien tranchées.

La syntaxe de cette fonction est plutôt simple. Elle prendra deux paramètres :

  • Premier paramètre: Un nombre entier représentant le nombre de "marche" de la transition.
  • Deuxième paramètre: Un mot clé qui définira si la propriété subissant la transition sera modifié au début ou à la fin du délai de chaque marche. Le mot clé sera start (au début) ou end (à la fin).

Ainsi, le graphique représentant les fonctions steps(3,start) et steps(3,end) serait le suivant:

[![Texte alternatif:!:graphique représentant les marches de la fonction CSS steps]!]

La fonction steps() pourrait être utilisé pour imiter l'action d'une machine à écrire, ou alors pour animer une image.

Conclusion

Vous voilà, je l'espère, un peu plus éclairé sur l'utilisation des effets de timing, et des transitions/animations en général. Comme nous le constatons de jour en jour, les outils que nous offre CSS3 sont extrêmement puissant et nous n'avons pas encore terminé d'en faire le tour. Mais prenez garde, ce n'est pas parce que les animations existent qu'il faut en mettre partout ! Évitons de retomber dans le web extravagant du début des années 2000...

Entre temps, voici quelques démonstrations et outils pouvant vous inspirer :