[FR]–Introduction aux transitions CSS3 - Eternal Coding - HTML5 / JavaScript / 3D development - Site Home - MSDN Blogs

[FR]–Introduction aux transitions CSS3


 

[FR]–Introduction aux transitions CSS3

  • Comments 3

Test2 

Une bonne application doit fournir du feedback visuel à ses utilisateurs. En effet, les utilisateurs doivent constamment être conscients que l’application a bien reçu et bien compris un ordre donné (par clic, tactile ou autre). Pour ce genre de communication, les animations constituent un excellent moyen de fournir un retour explicite.

La nouvelle spécification HTML5 (en fait pour être précis il faudrait dire la nouvelle spécification CSS3) introduit un nouvel outil pour gérer les animations simples : Les transitions.

Selon la spécification “CSS Transition Module Level 3” du site du W3C, les transitions CSS3 permettent de faire changer les valeurs de propriétés de CSS de manière fluide au cours d’une période donnée.

Le but de cet article va donc être tout d’abord de décrire le concept des transitions puis de voir comment les transitions CSS3 fonctionnent et comment nous allons pouvoir gérer les navigateurs qui ne supporteraient pas la fonctionnalité :HTML5 Powered with CSS3 / Styling

  1. Transitions CSS3
  2. Fonctionner partout
  3. Transitions sans les transitions CSS3   
  4. Conclusion
  5. Pour aller plus loin

De plus, je me permets de vous suggérer la lecture de “Introduction aux animations CSS3” (par David Rousset) qui constitue un excellent complément à la lecture de cet article.

Pour démontrer l’usage des transitions CSS3, j’ai écris un petit jeu qui les utilise pour animer les cellules d’un puzzle (de type Taquin) and qui utilise également le fallback que nous découvrirons par la suite dans le cas ou les transitions CSS3 ne seraient pas supportées par votre navigateur :

Pour lancer le jeu, vous avez juste à cliquer sur “Mix it!” and essayer de résoudre le puzzle en cliquant sur les cellules

Le code du jeu est disponible ici.

Transitions CSS3

Introduction

A l’origine, le groupe de travail du W3C sur CSS n’était pas trop enclin à ajouter les transitions dans les CSS en argumentant que les transitions n’étaient pas réellement des styles pour les propriétés. Mais finalement, les designers et les développeurs finirent par les convaincre que les transitions n’étaient en fin de compte que des styles dynamiques et cela permit donc l’apparition des transitions CSS3.

Selon le site du W3C, ces transitions sont capables d’animer les types de propriétés suivantes : (cliquez ici pour faire apparaitre la liste)

  • couleur : interpolée via ses composantes rouge, verte, bleue et alpha (qui sont considérées comme des nombres)
  • longueur : interpolée comme un nombre
  • pourcentage : interpolée comme un nombre
  • entier :interpolé de manière discrète (en fait l’interpolation se fait sur une version décimale qui est ensuite tronquée vers un entier en utilisant floor())
  • nombre: interpolé directement comme un réel
  • liste de transformation : voir la spécification des CSS3 2D Transforms (http://www.w3.org/TR/css3-2d-transforms)
  • rectangle: interpolé via ses composantes x, y, largeur, hauteur
  • visibilité: interpolé de manière discrète (en fait l’interpolation est effectuée sur un nombre réel allant de 0 à 1 ou 0 vaut caché et 1 vaut visible)
  • ombre: interpolé via ses composantes de couleur, x, y et flou
  • gradient: interpolé au travers de la position et la couleur de chacune des étapes. La valeur d’origine et la valeur de destination de la transitions doivent par contre avoir le même type (linéaire ou radial) et le même nombre d’étapes
  • paint server (SVG): l’interpolation est uniquement supporté de gradient ver gradient ou de couleur vers couleur
  • liste séparée par des espaces d’un type ci-dessus : si les listes d’origine et de destination de la transition ont le même nombre d’éléments, les règles ci-dessus s’applique et l’interpolation a bien lieu. Dans le cas contraire, l’interpolation n’est pas jouée
  • propriété synthétique (shorthand property) : si toutes les parties de la synthèse peuvent être animées alors l’interpolation a lieu comme si chaque propriété avait été spécifiée individuellement

Et les propriétés suivantes sont supportées par les transitions :(cliquez ici pour faire apparaitre la liste)

  • background-color (couleur)
  • background-image (que les gradients)
  • background-position (pourcentage et longueur)
  • border-bottom-color (couleur)
  • border-bottom-width (longueur)
  • border-color (couleur)
  • border-left-color (couleur)
  • border-left-width (longueur)
  • border-right-color (couleur)
  • border-right-width (longueur)
  • border-spacing (longueur)
  • border-top-color (couleur)
  • border-top-width (longueur)
  • border-width (longueur)
  • bottom (longueur et pourcentage)
  • color (couleur)
  • crop (rectangle)
  • font-size (longueur et pourcentage)
  • font-weight (nombre)
  • grid-* (divers)
  • height (longueur et pourcentage)
  • left (longueur et pourcentage)
  • letter-spacing (longueur)
  • line-height (nombre, longueur et pourcentage)
  • margin-bottom (longueur)
  • margin-left (longueur)
  • margin-right (longueur)
  • margin-top (longueur)
  • max-height (longueur et pourcentage)
  • max-width (longueur et pourcentage)
  • min-height (longueur et pourcentage)
  • min-width (longueur et pourcentage)
  • opacity (nombre)
  • outline-color (couleur)
  • outline-offset (entier)
  • outline-width (longueur)
  • padding-bottom (longueur)
  • padding-left (longueur)
  • padding-right (longueur)
  • padding-top (longueur)
  • right (longueur et pourcentage)
  • text-indent (longueur et pourcentage)
  • text-shadow (ombre)
  • top (longueur et pourcentage)
  • vertical-align (keywords, longueur et pourcentage)
  • visibility (visibilité)
  • width (longueur et pourcentage)
  • word-spacing (longueur et pourcentage)
  • z-index (entier)
  • zoom (nombre)

SVG

Les propriétés des entités SVG sont animables si elles sont définies en tant que animatable:true au niveau de la spécification SVG : http://www.w3.org/TR/SVG/struct.html.

Déclarations

Pour déclarer une transition dans un fichier CSS, vous devez juste écrire le code suivant :

  1. transition-property: all;
  2. transition-duration: 0.5s;
  3. transition-timing-function: ease;
  4. transition-delay: 0s;


Cette déclaration définit que chaque modification sur une propriété sera fait de manière évolutive en 0.5s (et non pas immédiatement donc Sourire). 

Vous pouvez aussi définir vos transitions par propriété :

  1. transition-property: opacity left top;
  2. transition-duration: 0.5s 0.8s 0.1s;
  3. transition-timing-function: ease linear ease;
  4. transition-delay: 0s 0s 1s;

Et finalement, il est aussi possible d’utiliser une propriété de synthèse “transition” qui permet de tout rassembler en une seule ligne :

  1. transition: all 0.5s ease 0s;

Vous pouvez préciser autant de propriétés que vous voulez sur cette version, en les séparant par une virgule :

  1. transition: opacity 0.5s ease 0s, left 0.8s linear 0s;

Les transitions sont activées dès que l’on modifie une propriété d’un objet cible. Cette modification peut être faite via JavaScript ou en en utilisant directement CSS3 en affectant une nouvelle classe CSS à une balise.

Par exemple, en utilisant IE10, si vous avez la déclaration CSS3 suivante :

  1. -ms-transition-property: opacity left top;
  2. -ms-transition-duration: 0.5s 0.8s 0.5s;
  3. -ms-transition-timing-function: ease linear ease;

Dès que vous modifierez l’opacité de votre balise, la valeur courant sera animée vers la nouvelle valeur en 0.5s en utilisant une fonction de cadencement de type “ease” (ce qui donne un impression de fluidité).

Transitions non linéaires

La propriété “transition-timing-function” définit que la transition ne sera pas linéaire mais qu’elle utilisera une fonction de cadencement pour produire une animation non-linéaire.

CSS3 transitions va utiliser une courbe de Bézier cubique pour “fluidifier” la transition en calculant une vitesse de progression différente tout au long de sa durée.

Les valeurs suivantes sont supportées:    

  • linear: Vitesse constante (comportement par défaut)
  • cubic-bezier: La vitesse sera calculée grâce à une courbe de Bézier cubique définie par deux points de contrôles : P0 et P1 (Il faudra donc fournir 4 paramètres : P0x, P0y, P1x et P1y).
  • ease: La vitesse sera calculée avec la formule suivante : cubic-bezier(0.25, 0.1, 0.25, 1)
  • ease-in: La vitesse sera calculée avec la formule suivante : cubic-bezier(0.42, 0, 1, 1)
  • ease-inout: La vitesse sera calculée avec la formule suivante : cubic-bezier(0.42, 0, 0.58, 1)
  • ease-out: La vitesse sera calculée avec la formule suivante : cubic-bezier(0, 0, 0.58, 1)

Pour mieux visualiser tout cela, j’ai écris un petit simulateur (en SVG bien évidemment Sourire) :

                      

Ce simulateur est écrit uniquement en JavaScript pour faciliter la compréhension des fonctions de cadencement :

  1. TRANSITIONSHELPER.computeCubicBezierCurveInterpolation = function (t, x1, y1, x2, y2) {
  2.     // Extract X (which is equal to time here)
  3.     var f0 = 1 - 3 * x2 + 3 * x1;
  4.     var f1 = 3 * x2 - 6 * x1;
  5.     var f2 = 3 * x1;
  6.  
  7.     var refinedT = t;
  8.     for (var i = 0; i < 5; i++) {
  9.         var refinedT2 = refinedT * refinedT;
  10.         var refinedT3 = refinedT2 * refinedT;
  11.  
  12.         var x = f0 * refinedT3 + f1 * refinedT2 + f2 * refinedT;
  13.         var slope = 1.0 / (3.0 * f0 * refinedT2 + 2.0 * f1 * refinedT + f2);
  14.         refinedT -= (x - t) * slope;
  15.         refinedT = Math.min(1, Math.max(0, refinedT));
  16.     }
  17.  
  18.     // Resolve cubic bezier for the given x
  19.     return 3 * Math.pow(1 - refinedT, 2) * refinedT * y1 +
  20.             3 * (1 - refinedT) * Math.pow(refinedT, 2) * y2 +
  21.             Math.pow(refinedT, 3);
  22. };

Ce code est une implémentation directe de la formule des courbes de Bézier cubiques. Vous pouvez télécharger le code directement ici.

Délai

La propriété “transition-delay” définit le délai entre le moment ou une valeur est modifiée et le moment ou la transition doit commencer.

Evènements

Un évènement est levé à la fin de chaque transition : “TransitionEnd”. Toutefois, pour le moment, en fonction du navigateur, il faudra choisir le bon nom dans cette liste:

  • Chrome & Safari: webkitTransitionEnd
  • Firefox: mozTransitionEnd
  • Opera: oTransitionEnd
  • Internet Explorer: MSTransitionEnd

Cet évènement fournit les informations suivantes:

  • propertyName: Nom de la propriété qui a été animée
  • elapsedTime: La durée de la transition en secondes

Voici un exemple de mise en place de l’évènement avec IE10:

  1. block.addEventListener("MSTransitionEnd", onTransitionEvent);

  
Intérêt des transitions CSS3

Je peux principalement trouvé deux grandes catégories de raisons qui justifient pleinement l’intérêt des transitions CSS3:

  • Accélération matérielle : Les transitions CSS3 sont directement gérées par le GPU (lorsque bien sur le système le supporte) ce qui permet d’avoir des résultats encore plus fluides et surtout qui ne consomment pas de puissance sur le CPU. C’est un point très important notamment dans le cadre du développement sur mobiles ou la puissance disponible doit être bien utilisée
  • Meilleure segmentation entre le code et le design : Pour un développeur ne doit pas avoir à se préoccuper des animations ou de tout ce qui est en rapport avec le design. De la même manière, les artistes ne doivent pas avoir à mettre les mains dans le JavaScript. C’est pour cette raison que les transitions CSS3 sont importantes car elles permettent de transférer la responsabilité des animations vers les équipes graphiques en utilisant le support du CSS
            

Support and gestion d’une technique de remplacement

Depuis la PP3, IE10 (que vous pouvez télécharger avec la Windows “8” Developer Preview ici) supporte les transitions CSS3:

image

Ce rapport a été généré par le site http://caniuse.com/#search=CSS3 transitions.

Bien évidemment comme la spécification est encore en état de “working draft” , vous allez devoir utiliser les préfixes de chaque browser tels que –ms-, –moz-, –webkit-, –o-.

Si l’on veut produire une solution fonctionnant partout de manière transparente il va falloir développer une petite API côté JavaScript qui s’appuiera sur une technique de détection pour proposer une solution de repli si jamais le navigateur ne supporte pas les transitions CSS3.

Il est bien sur possible de ne pas faire cela et dans ce cas, le site proposera une version dégradée (sans animation) mais fonctionnelle. On parlera ici de “progressive enhancement" ou amélioration progressive en ce sens que les navigateurs les plus puissants proposeront la meilleure expérience sans pour autant bloquer le fonctionnement sur les navigateurs moins puissants.

Transitions sans les transitions CSS3

Si nous souhaitons supporter les transitions partout nous allons devoir développer un petit outil qui se chargera de mimiquer le fonctionnement des transitions CSS3 mais uniquement par code (adieu accélération matérielle et autre séparation code/design mais bonjour la portabilité Sourire).

Donc tout d’abord nous allons créer un espace de nom pour contenir les informations et les fonctions de notre outils :

  1. var TRANSITIONSHELPER = TRANSITIONSHELPER || {};
  2.  
  3. TRANSITIONSHELPER.tickIntervalID = 0;
  4.  
  5. TRANSITIONSHELPER.easingFunctions = {
  6.     linear:0,
  7.     ease:1,
  8.     easein:2,
  9.     easeout:3,
  10.     easeinout:4,
  11.     custom:5
  12. };
  13.  
  14. TRANSITIONSHELPER.currentTransitions = [];

Pour avoir le même niveau de fonctionnalités que les fonctions de cadencement nous avons déclarer une énumération qui servira à choisir la fonction active pour une transition.

De manière générale l’outil est basé sur une fonction qui sera appelée toutes les 17ms (afin de tenter d’approcher le plus possible la barrière des 60 images par seconde). Cette fonction parcourra la liste des transitions actives et pour chacune d’entre elles évaluera la prochaine valeur à affecter.

Nous allons donc avoir besoin de fonctions pour extraire la valeur courante d’une propriété et l’unité utilisée : 

 
         
  1. TRANSITIONSHELPER.extractValue = function (string) {
  2.     try {
  3.         var result = parseFloat(string);
  4.  
  5.         if (isNaN(result)) {
  6.             return 0;
  7.         }
  8.  
  9.         return result;
  10.     } catch (e) {
  11.         return 0;
  12.     }
  13. };
  14.  
  15. TRANSITIONSHELPER.extractUnit = function (string) {
  16.  
  17.     // if value is empty we assume that it is px
  18.     if (string == "") {
  19.         return "px";
  20.     }
  21.  
  22.     var value = TRANSITIONSHELPER.extractValue(string);
  23.     var unit = string.replace(value, "");
  24.  
  25.     return unit;
  26. };

La fonction principale quant à elle va donc traiter les transitions actives et va devoir évaluer une courbe de Bézier cubique pour interpoler les nouvelles valeurs :

  1. TRANSITIONSHELPER.tick = function () {
  2.     // Processing transitions
  3.     for (var index = 0; index < TRANSITIONSHELPER.currentTransitions.length; index++) {
  4.         var transition = TRANSITIONSHELPER.currentTransitions[index];
  5.  
  6.         // compute new value
  7.         var currentDate = (new Date).getTime();
  8.         var diff = currentDate - transition.startDate;
  9.  
  10.         var step = diff / transition.duration;
  11.         var offset = 1;
  12.  
  13.         // Timing function
  14.         switch (transition.ease) {
  15.             case TRANSITIONSHELPER.easingFunctions.linear:
  16.                 offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0, 0, 1.0, 1.0);
  17.                 break;
  18.             case TRANSITIONSHELPER.easingFunctions.ease:
  19.                 offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0.25, 0.1, 0.25, 1.0);
  20.                 break;
  21.             case TRANSITIONSHELPER.easingFunctions.easein:
  22.                 offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0.42, 0, 1.0, 1.0);
  23.                 break;
  24.             case TRANSITIONSHELPER.easingFunctions.easeout:
  25.                 offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0, 0, 0.58, 1.0);
  26.                 break;
  27.             case TRANSITIONSHELPER.easingFunctions.easeinout:
  28.                 offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0.42, 0, 0.58, 1.0);
  29.                 break;
  30.             case TRANSITIONSHELPER.easingFunctions.custom:
  31.                 offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, transition.customEaseP1X, transition.customEaseP1Y, transition.customEaseP2X, transition.customEaseP2Y);
  32.                 break;
  33.         }
  34.  
  35.         offset *= (transition.finalValue - transition.originalValue);
  36.  
  37.         var unit = TRANSITIONSHELPER.extractUnit(transition.target.style[transition.property]);
  38.         var currentValue = transition.originalValue + offset;
  39.  
  40.         transition.currentDate = currentDate;
  41.  
  42.         // Dead transition?
  43.         if (currentDate >= transition.startDate + transition.duration) {
  44.             currentValue = transition.finalValue; // Clamping
  45.             TRANSITIONSHELPER.currentTransitions.splice(index, 1); // Removing transition
  46.             index--;
  47.  
  48.             // Completion event
  49.             if (transition.onCompletion) {
  50.                 transition.onCompletion({propertyName:transition.property, elapsedTime:transition.duration});
  51.             }
  52.         }
  53.  
  54.         // Affect it
  55.         transition.target.style[transition.property] = currentValue + unit;
  56.     }
  57. };

Il est important de noter que la version courant de l’outil ne gère que les valeurs numériques mais il reste simple de rajouter les valeurs complexes car elle peuvent subdiviser en valeurs simples (cf. plus haut avec la liste des types supportés).

Ainsi enregistrer une nouvelle transition dans le système se fait via le code suivant :

  1. TRANSITIONSHELPER.transition = function (target, property, newValue, duration, ease, customEaseP1X, customEaseP1Y, customEaseP2X, customEaseP2Y, onCompletion) {
  2.  
  3.     // Create a new transition
  4.     var transition = {
  5.         target: target,
  6.         property: property,
  7.         finalValue: newValue,
  8.         originalValue: TRANSITIONSHELPER.extractValue(target.style[property]),
  9.         duration: duration,
  10.         startDate: (new Date).getTime(),
  11.         currentDate: (new Date).getTime(),
  12.         ease:ease,
  13.         customEaseP1X:customEaseP1X,
  14.         customEaseP2X:customEaseP2X,
  15.         customEaseP1Y: customEaseP1Y,
  16.         customEaseP2Y: customEaseP2Y,
  17.         onCompletion: onCompletion
  18.     };
  19.  
  20.     // Launching the tick service if required
  21.     if (TRANSITIONSHELPER.tickIntervalID == 0) {
  22.         TRANSITIONSHELPER.tickIntervalID = setInterval(TRANSITIONSHELPER.tick, 17);
  23.     }
  24.     
  25.     // Remove previous transitions on same property and target
  26.     for (var index = 0; index < TRANSITIONSHELPER.currentTransitions.length; index++) {
  27.         var temp = TRANSITIONSHELPER.currentTransitions[index];
  28.  
  29.         if (temp.target === transition.target && temp.property === transition.property) {
  30.             TRANSITIONSHELPER.currentTransitions.splice(index, 1);
  31.             index--;
  32.         }
  33.     }
  34.  
  35.     // Register
  36.     if (transition.originalValue != transition.finalValue) {
  37.         TRANSITIONSHELPER.currentTransitions.push(transition);
  38.     }
  39. };

Notons que la fonction “tick” est lancée lors de l’enregistrement de la première transition.

Donc au final si l’on rajoute un zeste de modernizr pour nous aider à détecter si les transitions CSS3 sont supportées, nous sommes quasiment au bout.

Vous pouvez donc télécharger le code pour le TransitionsHelper à cet endroit : http://www.catuhe.com/msdn/transitions/transitionshelper.js 

Pour prendre un exemple de mise en œuvre, nous pouvons voir dans le JavaScript du puzzle que lorsqu’il s’agit de bouger les cellules, le code suivant est utilisé :  

  1. if (!PUZZLE.isTransitionsSupported) {
  2.     TRANSITIONSHELPER.transition(block.div, "top", block.x * totalSize + offset, 500, TRANSITIONSHELPER.easingFunctions.ease);
  3.     TRANSITIONSHELPER.transition(block.div, "left", block.y * totalSize + offset, 500, TRANSITIONSHELPER.easingFunctions.ease);
  4. }
  5. else {
  6.     block.div.style.top = (block.x * totalSize + offset) + "px";
  7.     block.div.style.left = (block.y * totalSize + offset) + "px";
  8. }

Ainsi, soit nous changeons uniquement les valeurs des propriétés en sachant que les transitions CSS3 sont en place et les animeront soit nous nous replions sur l’outil pour lancer les transitions.

Il est important également de noter qu’il y aurait une autre manière d’animer les cellules quand les transitions sont supportées : En effet, il est possible plutôt que d’affecter des valeurs à des propriétés de changer la classe CSS de la balise cible. Il suffirait dans le cadre du puzzle d’avoir 9 classes prédéfinies (une pour chaque case) qui se chargeraient pour nous d’affecter les valeurs. Cette solution a l’avantage encore une fois de transférer la responsabilité du design vers le CSS.

Pour conclure ce chapitre sur les méthodes de repli, voici quelques frameworks et boite à outils qui supportent les transitions par code :

            

Conclusion

Comme nous l’avons vu, les transitions CSS3 constituent un moyen très simple d’ajouter de la vie et de la réactivité à votre application.

Ces transitions peuvent être intégrer à votre projet de deux manières si vous souhaitez en plus gérer des solutions de repli :

  • Vous pouvez tout faire en JavaScript and si vous détectez le support des transitions CSS3 alors vous injectez à la volée du CSS3 dans votre page. Cette solution peut être intéressante dans une approche.
  • Ou alors vous pouvez utiliser la technique présentée dans cet article (se basant à la fois sur les déclarations CSS et sur JavaScript lors de l’absence de support). Pour ma part, je considère que c’est la meilleure méthode car JavaScript ne doit servir que pour le repli car dans un futur proche il ne devrait plus être utile quand tous les navigateurs supporteront la norme CSS3. De plus comme vu plus haut, c’est une meilleure idée de laisser le contrôle sur les animations à l’équipe graphique.

Aller plus loin

Leave a Comment
  • Please add 7 and 6 and type the answer here:
  • Post
  • tutoriels très complet merci a vous

  • excellent tuto :)

    un grand merci ++

  • Merci beaucoup pour ce tuto.

    Excellent et complet.

Page 1 of 1 (3 items)