• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 07/02/2024

Manipulez et réutilisez les animations CSS

Ça fait deux chapitres que je vous en parle, vous devez commencer à saisir le potentiel des animations @keyframes, n'est-ce pas ?

Nous avons maintenant tous les outils nécessaires pour créer et affiner la plupart des animations auxquelles nous pouvons penser. Mais...

Et si on voulait faire une animation de quelque chose qui rebondit indéfiniment d'avant en arrière ?

Carré qui se déplace de gauche à droite

Nous pourrions bien sûr créer une animation avec BEAUCOUP de keyframes... 

Et si nous voulions que des éléments différents tournent dans des directions opposées ? Ou à des vitesses différentes ?

Carrés qui effectuent des rotations à différentes vitesses et dans différents sens

Nous pourrions créer plusieurs animations @keyframes, une avec une rotation dans le sens des aiguilles d'une montre et l'autre dans le sens inverse, une autre avec une vitesse différente, etc.

Je préfère vous le dire tout de suite : si vous préférez ces solutions, ce sera sans moi 🙃. Car si je vous en parle, c'est qu'il existe bien sûr une solution adaptée. En quelques propriétés seulement, nous pouvons dire au navigateur de le répéter pour nous. Ou de le lire à l'envers. Ou même de faire une pause et de le jouer sur commande.

Nous allons voir ensemble comment tirer le meilleur parti des animations @keyframes, tout en gardant la base de code la plus propre et maintenable possible.

Repensez votre validation d'email

Remontons dans le temps ⏱, à la partie 1, chapitre 3 et au monde des transitions et des pseudoclasses. Cette époque où vous ne connaissiez pas encore les keyframes. Nous avions créé un input d'adresse e-mail qui donnait à l'utilisateur un feedback visuel si l'adresse entrée n'était pas valide :

 Champs email qui devient rouge si l'email n'est pas valide 

C'était déjà pas mal du tout. L'utilisateur peut voir en un coup d’œil qu'il y a quelque chose qui cloche. Mais le fondu entre les états valide et non valide reste plutôt soft.

Nous pouvons maintenant tirer parti des keyframes pour créer une animation encore plus impactante.

Pour vous rafraîchir la mémoire, ici notre élément input est un input standard, dont le type est défini sur email :

<html>
    <div class="container">
        <div class="form">
            <div class="form__group">
                <label for="email-input">email</label>
                    <input type="email" name=”email-input>
                </div>
            </div>
        </div>
</html>

 Notre animation change actuellement la  background-color  pour un rouge foncé sur une durée de 500 ms si l'adresse saisie n'est pas valide :

$cd-txt: #6300a0;
$cd-box: #fff;
$cd-txt--invalid: #fff;
$cd-danger : #b20a37 ;
.form {
    &__group {
        & input {
            border: 2px solid $cd-box;
            border-radius: 100rem;
            color: $cd-txt;
            font-family: 'Montserrat', sans-serif;
            font-size: 2.5rem;
            outline: none;
            padding: .5rem 1.5rem;
            width: 100%;
            transition: background-color 500ms;
            &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                background-color: $cd-danger;
                border: 2px solid $cd-danger;
                color: $cd-txt--invalid;
            }
        }
        
    }
}

Le style de base est tout à fait correct, il vaut mieux donc mieux ne pas y toucher. Au lieu de cela, nous allons supprimer la transition du sélecteur d'entrée. Au revoir tout notre beau travail ! Il est parfois très difficile d'avoir à supprimer du travail sur lequel on avait passé du temps pour se lancer dans une autre direction, mais cela fait juste partie du processus créatif. Parfois, quelque chose ne fonctionne pas aussi bien que nous l'envisagions, et nous devons savoir l'abandonner. Alors... adieu transition  background-color

$cd-txt: #6300a0;
$cd-box: #fff;
$cd-txt--invalid: #fff;
$cd-danger : #b20a37 ;

.form {
    &__group {
        & input {
            border: 2px solid $cd-box;
            border-radius: 100rem;
            color: $cd-txt;
            font-family: 'Montserrat', sans-serif;
            font-size: 2.5rem;
            outline: none;
            padding: .5rem 1.5rem;
            width: 100%;
            &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                background-color: $cd-danger;
                border: 2px solid $cd-danger;
                color: $cd-txt--invalid;
           }
        }
    }
}

Nous avons désormais un input d'e-mail qui devient rouge lorsque l'entrée n'est pas valide et ne sera pas corrigée : Champ email qui devient rouge lorsque l'email n'est pas valideAlors maintenant, quoi ? Qu'est-ce qu'on pourrait faire de tellement mieux qui justifierait de jeter une transition parfaitement utilisable pour tout recommencer ?

Créez des émotions avec vos animations

Je vous vois. Vous avez hâte de savoir ce que l'on va créer ensemble.

L'idée est simple : Et si on faisait hocher la tête à notre input ?

C'est un signe quasiment universel que la plupart peuvent interpréter comme un « non ». En tant qu'humains, nous absorbons et nous traitons le langage corporel au niveau de l'inconscient, de sorte que même les mouvements subtils peuvent avoir un sens. Nous n'avons ainsi plus rien à faire. Un rapide hochement de tête, clair et ferme, c'est tout ce dont nous avons besoin.

Alors représentons notre "refus" avec l'animation @keyframes headshake :

@keyframes headshake {
    
}

Nous voulons que notre entrée commence et se termine là où elle devrait, et nous n'avons donc pas besoin de créer des keyframes pour le début et la fin. En les laissant en dehors des keyframes, notre animation utilisera les valeurs par défaut du sélecteur. Ça tombe bien,  ce dont nous avons vraiment besoin, ce sont des keyframes pour le mouvement intermédiaire.

Depuis son état neutre, nous voulons que l'input se déplace un peu vers la droite, puis vers la gauche, avant de revenir à sa position à son état neutre. Donc, à 0 %, nous sommes dans la position par défaut. Puis, à un quart de l'animation, nous voulons que l'entrée soit entièrement à droite. Et à 75 % dans l'animation, nous voulons qu'elle soit à l'opposé sur la gauche, avant de revenir enfin à sa position neutre :

@keyframes headshake {
    25% {
        // entièrement à droite
        transform: translateX();
    }
    75% {
        // entièrement à gauche
        transform: translateX();
    }
}

Il ne nous reste plus qu'à décider à quel moment nous voulons que notre input hoche la tête. Ça ne devrait pas être trop exagéré, juste un hochement de tête net et courtois. Un point de pourcentage ou deux devraient faire l'affaire. Créons une variable intitulée  $shake-intensity, réglons-la (soyons fous) sur une valeur de 2 %, et ajoutons-la à nos fonctions   translateX()  : 

$shake-intensity: 2%;
@keyframes headshake {
    25% {
        // entièrement à droite
        transform: translateX($shake-intensity);
    }
    75% {
        // entièrement à gauche
        transform: translateX(-$shake-intensity);
    }
}

Nous voulons que la position de notre input sur la gauche soit l'inverse de la droite. Nous avons donc juste préfixé la variable avec un symbole négatif. Désormais, nous devons ajouter nos  @keyframes headshake au sélecteurinput:not(:focus):invalid.  Nous voulons un hochement de tête brusque et rapide, alors donnons-lui une valeur courte pour sa durée. Quelque chose aux alentours des 100 ms devrait faire l'affaire, au début du moins : 

$cd-danger : #b20a37 ;
$cd-txt: #6300a0;
$shake-intensity: 2%;

.form {
    &__group {
        & input {
            &:active, &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                color: white;
                border: 2px solid $cd-danger;
                background: $cd-danger;
                animation: headshake 100ms cubic-bezier(.4,.1,.6,.9);
            }
        }
    }
}

@keyframes headshake {
    25% {
        // entièrement à droite
        transform: translateX($shake-intensity);
    }
    75% {
        // entièrement à gauche
        transform: translateX($shake-intensity * -1);
    }
}

Nous avons également réglé la fonction  animation-timing-function  pour le headhake sur cubic-bezier(.4,.1,.6,.9), ce qui reste plutôt léger :

Le cubic-bézier légèrement modifié

Testons la réaction que cela nous donne !

Champs d'email qui devient rouge lorsque l'email n'est pas valide

Ça, c'est un beau hochement de tête anthropomorphique ! Si beau d'ailleurs qu'on ne s'en lasse pas : on voudrait en voir plus.

OK ! Donc, ça veut dire que nous devons encore ajouter d'autres keyframes. Pour obtenir deux hochements de tête, l'extrême droite devrait être à... 12,5 % et 62,5 %... et la gauche devrait être à 37,5 % et 87,5 %... c'est ça ? Et si ça ne suffisait toujours pas pour un bon hochement de tête ? Et si on avait besoin de trois hochements de tête ? Ça voudrait dire que l'extrême droite serait à... juste une seconde... passer à... euh... vous savez quoi ? Il doit y avoir un meilleur moyen. Parce qu'il y a toujours un meilleur moyen !

Et ce meilleur moyen, c'est quoi ?

La propriété  animation-iteration-count ! Elle nous permet d'écrire des keyframes d'un seul cycle d'animation et demander au navigateur de les répéter autant de fois que nous le souhaitons. Cela signifie que nous pouvons faire secouer la tête de notre input deux fois, ou deux cents fois, en changeant une seule valeur :

$cd-danger : #b20a37 ;
$cd-txt: #6300a0;
$shake-intensity: 2%;

.form {
    &__group {
        & input {
            &:active, &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                color: white;
                border: 2px solid $cd-danger;
                background: $cd-danger;
                animation: headshake 100ms cubic-bezier(.4,.1,.6,.9);
                animation-iteration-count: 3;
            }
        }
    }
}

@keyframes headshake {
    25% {
        // entièrement à droite
        transform: translateX($shake-intensity);
    }
    75% {
        // entièrement à gauche
        transform: translateX($shake-intensity * -1);
    }
}

Il y a un principe d'écriture appelé la « règle de trois » selon laquelle une chose qui arrive trois fois serait plus efficace et impactante. Que ce soit plus dramatique, plus drôle ou plus percutant (vous avez vu, je l'ai utilisée ici). Alors nous avons donné une valeur de trois à la propriété  animation-iteration-count.

Champs d'email qui devient rouge et qui tremble lorsque l'email n'est pas valide

Meh. Je ne suis pas convaincue. Deux c'était très bien aussi. Réduisons donc le nombre d'itérations à 2 et testons à nouveau l'animation. Et quand on les voit enchaînés comme ça, 2 % d'intensité pour le hochement semble également un peu trop. Descendons à 1 % pendant qu'on y est :

$cd-danger : #b20a37 ;
$cd-txt: #6300a0;
$shake-intensity: 1%;

.form {
    &__group {
        & input {
            &:active, &:focus {
                border: 2px solid $cd-txt;
            }
            &:not(:focus):invalid {
                color: white;
                border: 2px solid $cd-danger;
                background: $cd-danger;
                // animation-iteration-count est directement intégré dans la propriété raccourcie animation:
                animation: headshake 100ms cubic-bezier(.4,.1,.6,.9) 2;
            }
        }
    }
}

@keyframes headshake {
    25% {
        // entièrement à droite
        transform: translateX($shake-intensity);
    }
    75% {
        // entièrement à gauche
        transform: translateX($shake-intensity * -1);
    }
}

Où est passé  animation-iteration-count   ? Comme pour les autres propriétés d'animation, nous l'avons inclus dans la propriété d'animation abrégée, plutôt que d'écrire la version explicite. Nous avons donc ajouté le chiffre 2 à la liste des valeurs pour l'animation, et maintenant notre entrée devrait hocher la tête deux fois.

Cette valeur pour la durée de l'animation s'applique à la durée d'un seul cycle d'animation. Cela signifie que notre animation de hochement de tête, avec une durée assignée de 100 ms et un nombre d'itérations de 2, prendra un total de 200 ms à se terminer :

Champs d'email qui devient rouge et qui tremble lorsque l'email n'est pas valide

Ça c'est la bonne durée pour un hochement de tête ! 🎉

Avec  animation-iteration-count, nous pouvons nous épargner beaucoup de calcul mental et de keyframes compliqués en écrivant un seul cycle d'animation et en demandant au navigateur de le rejouer autant de fois que nous le voulons. Bien plus propre, et bien plus facile à retoucher et à affiner.

Mais comment faire si on veut qu'une animation joue en boucle à l'infini ?

Créez votre premier loader

Les sites web appellent souvent le serveur en arrière-plan pour demander des données, comme une image, un texte, une donnée, etc. Pendant que la page attend la réponse du serveur, elle affiche un espace réservé.

La réponse du serveur ne prend parfois qu'une fraction de seconde ou beaucoup plus, selon la complexité de la demande, la taille des données transférées et la vitesse de connexion. Or, sur la plupart des interfaces, nous ne connaissons pas le temps nécessaire pour obtenir une réponse du serveur. La barre de progression du chapitre précédent n'est pas forcément adaptée à cet usage. Nous allons donc construire quelque chose de plus générique : un loader !

Pour commencer, créons une  <div>  avec une classe   .load,  et cinq divs enfants intégrées, chacune avec une classe de   .load__bar assignée   :

<html>
    <div class="container">
        <div class="load">
            <div class="load__bar"></div>
            <div class="load__bar"></div>
            <div class="load__bar"></div>
            <div class="load__bar"></div>
            <div class="load__bar"></div>
        </div>
     </div>
</html>

Maintenant que nous avons construit nos barres, nous devons les styliser pour qu'elles ressemblent à... eh bien... des barres ! Attribuons à  .load  les propriétés de layout appropriées, et configurons les dimensions pour  .load__bar, tout en leur donnant un joli  background-color  vert menthe :

$cd-bars: #15DEA5;
$size: 3vh;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
    }
}

En vérifiant dans notre navigateur, nous découvrons une magnifique rangée de cinq barres vert menthe !

Les barres sont immobiles

Super ! Mais pour le moment elles sont juste là, immobiles...

Nous sommes dans un cours d'animation ! Et nous voulons offrir à nos utilisateurs un peu de mouvement pour éveiller leur intérêt pendant qu'ils patientent. Alors créons un ensemble de @keyframes appelés bars et utilisons la fonction de transformation  scaleY()  pour animer l'échelle verticale de ces barres dans le temps, en commençant à 50 % et en terminant à 100 % :


@keyframes bars {
    0% {
        transform: scaleY(0.5);
        
    }
    100% {
        transform: scaleY(1.0);
    }
}

Bien sûr, pour que nos keyframes aient un effet sur nos barres et commencent à animer leur échelle, nous devons d'abord les appliquer au sélecteur .bars. Alors configurons une animation à l'aide de @keyframes bars et une durée de 1 000 ms :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur;
    }
}

@keyframes bars {
    0% {
        transform: scaleY(0.5);
    }
    100% {
        transform: scaleY(1.0);
    }
}

La variable   $anim-dur  correspond à la durée de notre animation. Comme nous l'avons vu, nous pouvons profiter du Sass pour créer des variables, ce qui nous permet d'ajuster nos valeurs un peu plus facilement, si nécessaire. Maintenant, nos barres devraient passer de la moitié de leur taille verticale à leur hauteur totale en une seconde :

Barres animées de loading

C'est parfait : ça fonctionne comme ce qu'on attendait... mais, vous me connaissez. C'est encore loin de me suffire... Rien de très fun dans cette animation pour le moment. Souvenez-vous des 12 principes : ajouter un peu de complexité à nos animations contribue grandement à les rendre attrayantes pour nos visiteurs. Nous pouvons alterner leur croissance, pour qu'elles grandissent les unes après les autres, un peu comme une vague.

À votre avis, quel est le meilleur moyen de créer des animations qui se déclenchent de manière alternée ?

La propriété   animation-delay, ça vous dit quelque chose ?

Nous avons cinq barres, donc nous avons besoin de cinq sélecteurs de modification, chacun avec un délai d'animation assigné légèrement plus long.

On pourrait les écrire manuellement, on pourrait faire beaucoup de choses. Mais pourquoi écrire des dizaines de lignes et créer des répétitions alors que nous pourrions utiliser Sass pour gérer cette tâche répétitive et incrémentale ?

Nous allons donc utiliser les boucles Sass.

Configurons une boucle Sass @for pour l'itération à travers un ensemble de nombres allant de un à cinq, en incrémentant le nom du sélecteur et le délai de l'animation :

$cd-bars: #15DEA5;
$size: 3vh;
$num-bars: 5
$anim-dur: 1000ms;
$anim-delay: $anim-dur / $num-bars;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur;
        @for $i from 1 through $num-bars {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
             }
        }
    }
}

Chaque itération à travers la boucle produira un nouveau sélecteur, avec le numéro d'index de l'itération utilisé dans son nom, ainsi que pour multiplier le  $anim-delay  et créer un délai de lancement qui sera incrémenté au fur et à mesure.  $anim-delay  est réglé sur la durée de l'animation divisée par le nombre de barres.

 Ainsi, lors de la première itération, le modificateur sera nommé  .load__bar--1  et aura un retard de 200 ms, le second sera  .load__bar--2  avec un retard de 400 ms, et ainsi de suite. En vérifiant le CSS compilé, nous voyons que les sélecteurs suivants viennent d'être créés :

.load__bar--1 {
    animation-delay: 200ms;
}
.load__bar--2 {
    animation-delay: 400ms;
}
.load__bar--3 {
    animation-delay: 600ms;
}
.load__bar--4 {
    animation-delay: 800ms;
}
.load__bar--5 {
    animation-delay: 1000ms;
}

Maintenant, il ne nous reste plus qu'à appliquer nos nouveaux modificateurs aux divs appropriées dans notre HTML :


<div class="container">
    <div class="load">
        <div class="load__bar load__bar--1"></div>
        <div class="load__bar load__bar--2"></div>
        <div class="load__bar load__bar--3"></div>
        <div class="load__bar load__bar--4"></div>
        <div class="load__bar load__bar--5"></div>
    </div>
 </div>

J'aimerais que nous prenions un instant pour admirer nos barres dans le navigateur :

Barre animées loading pas synchronisées

Nous avons ajouté tous nos délais, mais nous avons oublié de régler le mode de remplissage. Résultat : nos barres reviennent à leur taille initiale à la fin de l'animation.

Mais comment faire pour éviter ce problème ?

Réglons donc le mode  animation-fill-mode  sur backwards pour étendre les valeurs de départ de l'animation pendant le délai :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur backwards;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
        }
    }
}

Et voilà ! Admirez nos magnifiques barres ondulantes !

Barres animées ondulation

Du moins, admirez au moins UN cycle de nos barres ondulantes... Maintenant, répétons ce cycle, vers l'infini et au delà ! 👩‍🚀

Pour cela, utilisons  animation-iteration-count, qui est parfait pour ce genre de situation. Mais combien de cycles devons-nous utiliser dans les réglages pour le nombre d'itérations ?

Faites boucler vos animations à l'infini

Nous ne savons pas combien de temps va s'écouler entre le moment où notre navigateur envoie sa requête au serveur et la réception des données en réponse. Et cela signifie que nous ne savons jamais vraiment combien de fois nous aurons besoin de répéter l'animation de nos barres.

Nous pourrions leur attribuer un chiffre très élevé comme 9 999.... mais c'est une solution de contournement un peu facile et pas du tout optimisée...

Plutôt que de spécifier un nombre spécifique pour le nombre d'itérations, nous pouvons aussi dire au navigateur de lire l'animation à l'infini en lui assignant le mot clé « infinite » à la place :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur backwards infinite;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
        }
    }
}

Et maintenant voici notre animation répétant à l'infini une série de barres !

Barres animées iteration count

Maintenant qu'elles tournent en boucle, nous avons un problème très similaire à celui que nous avions avant d'ajouter le mode de remplissage backwards à notre animation : les barres passent subitement de leur taille finale à leur taille initiale. Sauf que cette fois, c'est parce que les valeurs de début et de fin de la boucle sont différentes. Notre cycle devrait en fait commencer par des barres plus petites, qui grandissent jusqu'à leur taille maximale au milieu de l'animation, avant de rétrécir à leur taille initiale à la fin.

Pour cela, nous pourrions ajouter un keyframe supplémentaire au milieu de l'animation de nos barres. Mais encore une fois... nous n'allons pas le faire. Ici aussi, il existe une meilleure solution. Et si nous voulions utiliser nos @keyframes bars ailleurs sur notre page, mais plutôt que de les animer en boucle, nous voulions qu'elles ne grandissent qu'une fois ? Si nous ajoutions ce keyframe supplémentaire à l'animation de nos barres, nous serions en train de modifier notre animation originale au service d'un cas particulier, et éventuellement de nous empêcher de l'utiliser dans d'autres animations sur notre site.

À la place, nous pouvons utiliser la propriété animation-direction pour transformer nos @keyframes en une boucle invisible, même si les valeurs de début et de fin des keyframes sont différentes ! 

La valeur par défaut pour animation-direction, celle qui est assignée si nous ne lui donnons pas explicitement une autre valeur, est « normal ». Comme vous auriez pu le supposer, ceci rejoue une animation normalement, c'est-à-dire du début à la fin :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur backwards infinite;
        animation-direction : normal;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
        }
    }
}

Généralement, il est très rare de voir animation-direction avec la valeur « normal », car il s'agit d'une option qui donne le même résultat que si nous ne l’avions pas précisé du tout :

Barres ondulantes rythmées

Là où l'utilisation de animation-direction commence à devenir très utile, c'est avec le mot clé « reverse ». Et qu'est-ce qu'on obtient en attribuant « reverse » à animation-direction ? Vous vous en doutez peut-être. L'animation est lue à l'envers, en commençant par la fin et en la lisant à l'envers jusqu'au début :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur backwards infinite;
        animation-direction: reverse;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
        }
    }
}

Et maintenant, l'animation de nos barres va être jouée à l'envers, ce qui n'est pas exactement ce que nous recherchons :

Barres ondulantes reverse

Non seulement animation-direction nous permet de jouer des animations en avant et en arrière, mais elle nous permet également de jouer des animations avec des allers-retours, de sorte que la direction de l'animation alterne avec chaque itération, d'abord avec une lecture du début à la fin, puis en alternant avec une lecture de la fin au début. Ça pourrait être pas mal dans le cas de notre loader.

Renseignons donc la valeur alternate :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    width: $size*10;
    height: $size*7.5;
    display: flex;
    justify-content: space-evenly;
    &__bar {
        background-color: $cd-bars;
        height: 100%;
        width: $size;
        animation: bars $anim-dur backwards infinite;
        animation-direction: alternate;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
        }
    }
}

Et maintenant, nous avons une animation en boucle infinie et invisible !

Barres ondulantes optimales

Nous voilà sur le bon chemin ! La fonction timing-function prévoit par défaut différentes pentes d'easing par rapport à ses profils d'accélération et de décélération, ce qui crée un effet d'à-coups entre les cycles :

Le cubic-bézier de l'animation

Au lieu de s'en tenir aux valeurs par défaut de la fonction animation-timing-function, assignons-lui quelque chose de plus symétrique, comme le profil ease-in-out :

Le cubic-bézier que nous souhaitons avoir

Et pendant que nous modifions le timing de notre animation, profitons-en pour abréger tout cela en utilisant le mot clé “animation”. Encore une fois, il suffit d’ajouter la valeur alternate à la liste des valeurs de la propriété animation :

$cd-bars: #15DEA5;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    &__bar {
        background-color: $cd-bars;
        animation: bars $anim-dur backwards infinite ease-in-out alternate;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
        }
    }
}

Maintenant, notre boucle devrait être beaucoup plus fluide :

Barres ondulantes encore plus fluides

Joliiiii 🤓 ! Alors, on s'arrête là ? ... Non, pas encore.

Pour le moment, l'animation reste quand même un peu ennuyeuse. Essayons de rendre l'ensemble un peu plus attrayant en ajoutant un peu plus de complexité visuelle. Peut-être que nous pouvons ajouter un autre ensemble de barres juste en dessous, et les animer en miroir ? Pour cela, nous pouvons simplement dupliquer la   div .load  et son contenu :

<div class="container">
    <div class="load">
        <div class="load__bar load__bar--1"></div>
        <div class="load__bar load__bar--2"></div>
        <div class="load__bar load__bar--3"></div>
        <div class="load__bar load__bar--4"></div>
        <div class="load__bar load__bar--5"></div>
    </div>
    <div class="load">
        <div class="load__bar load__bar--1"></div>
        <div class="load__bar load__bar--2"></div>
        <div class="load__bar load__bar--3"></div>
        <div class="load__bar load__bar--4"></div>
        <div class="load__bar load__bar--5"></div>
    </div>
</div>

Voyons où ça nous mène :

Barres ondulantes x2 non synchronisées

Mmm... c'est plutôt cool... Mais tentons de pimenter tout ça.Plutôt que de reproduire l'ensemble de barres du haut, essayons de faire en sorte que l'ensemble de barres du bas se fonde dans celui du haut, de façon à ce que la barre du haut s'agrandisse et celle du bas rétrécisse. Un peu comme une synchronisation inversée. Il nous faut une boucle continue pour l'animation, mais en sens inverse.

Dites donc, ne serait-ce pas génial s'il y avait un mot clé pour la propriété animation-direction qui jouerait les animations dans des directions alternées, mais à l'envers ? Bon, vous vous doutez sûrement maintenant que nous ne faisons pas les choses au hasard !

 La valeur s'appelle « alternate-reverse ». Afin d'appliquer la valeur  alternate-reverse  à notre deuxième ensemble de barres, nous devrons créer un nouvel ensemble de modificateurs, ce qui est possible depuis notre boucle @for. 

Au sein de chaque boucle, nous demanderons de créer un deuxième sélecteur, en utilisant la même structure de dénomination de base, mais en ajoutant un suffixe pour indiquer que sa direction est inversée. Pendant que nous y sommes, donnons aussi un peu plus de contraste visuel et ajoutons une couleur différente à nos barres inversées :

$cd-bars: #15DEA5;
$cd-bars-inv: #0E397F;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    &__bar {
        animation: bars $anim-dur backwards infinite ease-in-out alternate;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
            &--#{$i}-inv {
                animation-delay: $anim-delay * $i;
                animation-direction: alternate-reverse;
                background-color: $cd-bars-inv;
            }
        }
    }
}

Notre CSS compilé a désormais deux ensembles de modificateurs, un pour la ligne du haut, un autre pour le bas :

.load__bar--1 {
    animation-delay: 200ms;
}
.load__bar--1-inv {
    animation-delay: 200ms;
    animation-direction: alternate-reverse;
    animation-fill-mode: forwards;
    background: #0E397F;
}
.load__bar--2 {
    animation-delay: 400ms;
}
.load__bar--2-inv {
    animation-delay: 400ms;
    animation-direction: alternate-reverse;
    animation-fill-mode: forwards;
    background: #0E397F;
}
.load__bar--3 {
    animation-delay: 600ms;
    
}
.load__bar--3-inv {
    animation-delay: 600ms;
    animation-direction: alternate-reverse;
    animation-fill-mode: forwards;
    background: #0E397F;
}
.load__bar--4 {
    animation-delay: 800ms;
}
.load__bar--4-inv {
    animation-delay: 800ms;
    animation-direction: alternate-reverse;
    animation-fill-mode: forwards;
    background: #0E397F;
}
.load__bar--5 {
    animation-delay: 1000ms;
}
.load__bar--5-inv {
    animation-delay: 1000ms;
    animation-direction: alternate-reverse;
    animation-fill-mode: forwards;
    background: #0E397F;
}

Remplaçons les modificateurs pour notre deuxième ensemble de barres dans le HTML :

<div class="container">
    <div class="load">
    <div class="load__bar load__bar--1"></div>
    <div class="load__bar load__bar--2"></div>
    <div class="load__bar load__bar--3"></div>
    <div class="load__bar load__bar--4"></div>
    <div class="load__bar load__bar--5"></div>
</div>
<div class="load">
    <div class="load__bar load__bar--1-inv"></div>
    <div class="load__bar load__bar--2-inv"></div>
    <div class="load__bar load__bar--3-inv"></div>
    <div class="load__bar load__bar--4-inv"></div>
    <div class="load__bar load__bar--5-inv"></div>
    </div>
</div>

Et maintenant, vérifions comment nos barres synchronisées de façon inverse s'affichent dans notre navigateur :

Barres ondulantes synchronisées

Beaucoup mieux ! Nous avons ajouté une sorte de mouvement secondaire à notre animation en faisant en sorte que nos deux séries de barres soient complémentaires...

Faites une pause

Nos barres de chargement sont pas mal du tout. Nous pourrions très bien dire qu'elles sont terminées et passer à autre chose. Mais qu'en est-il de ces pauvres utilisateurs qui n’ont pas encore la fibre ? Qui sait combien de temps ils devront regarder ces barres avant d'obtenir une réponse du serveur ? Ayons pitié de leurs pauvres âmes et faisons quelque chose pour rendre l'animation un peu plus interactive et les divertir pendant qu'ils patientent.

Surprise ! Il y a une propriété d'animation CSS que nous n'avons pas encore abordée. Plutôt que de contrôler la direction ou le délai d'une animation, cette propriété permet de contrôler si l'animation est lue ou non. La propriété animation-play-state permet de mettre en pause ou de jouer des animations en utilisant respectivement les mots clés « paused » ou « running ».

Supposons que nous disposions d'une div toute simple avec la classe .spin :

<div class="container">
    <div class="spin"></div>
</div>

Quelques @keyframes et propriétés d'animation suffisent pour la faire tourner à l'infini, chaque rotation prenant trois secondes à se terminer :

.spin {
    background-color: #15DEA5;
    width: 30vh;
    height: 30vh;
    animation: spin 3s linear infinite;
}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

Carré qui pivote

Elle tourne, tourne, tourne... Plus rien ne peut l'arrêter, ni l'accélérer ou lui faire quoi que ce soit, à vrai dire. Pour l'instant, nous n'avons vu que comment mettre nos animations en mouvement, mais une fois qu'elles sont en marche, nous n'avons plus qu'à attendre qu'elles se terminent. Mais la propriété  animation-play-state  nous offre une commande toute prête que nous pouvons utiliser pour lire et mettre en pause nos animations.

Configurons notre boîte pour qu'elle commence en pause, mais que la lecture de l'animation commence au survol de la souris. Pour mettre en pause notre boîte en rotation, nous devons régler  animation-play-state  sur « paused », ou simplement ajouter le mot clé « paused » à la propriété d'animation abrégée :

.spin {
    background-color: #15DEA5;
    width: 30vh;
    height: 30vh;
    animation: spin 3s linear infinite paused;
}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

Maintenant, notre boîte ne bouge plus, même si notre animation de rotation « spin » lui est appliquée :

L'animation ne fonctionne plus

Notre animation commence directement en statut pause. Pour faire bouger notre animation, nous avons besoin d'ajouter un bouton de lecture, ce que nous allons faire en ajoutant un pseudosélecteur   :hover  à notre sélecteur   .spin, dans lequel l’état de l’animation sera sur « running » :

.spin {
    background-color: #15DEA5;
    width: 30vh;
    height: 30vh;
    animation: spin 3s linear infinite paused;
    &:hover {
        animation-play-state: running;
    }
}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

Maintenant, chaque fois que nous survolerons notre boîte, elle reprendra l'animation là où elle s'était arrêtée, et une fois le survol terminé, elle fera une pause là où elle était, pour recommencer son animation si nous la survolons à nouveau :

Carré pivotant au passage de la souris

animation-play-state  donne à nos utilisateurs la possibilité d'interagir avec nos animations 🔥. Ce qui pourrait être plutôt cool, à condition de ne pas en faire trop. Utilisons  animation-play-state  pour transformer nos barres de chargement en un petit jeu qui va aider nos utilisateurs à patienter en attendant la réponse du serveur.

Pour l'instant, nos deux ensembles de barres sont verrouillés l'un et l'autre dans une sorte de danse :

Barres ondulantes synchronisées

Et si on changeait ces délais sur la deuxième série de barres, pour que chacune ait un temps de départ aléatoire ?

Barres ondulantes délai

Maintenant, tout est désynchronisé ! Mais cela nous permet d'intégrer une certaine interactivité pour nos utilisateurs et de les divertir un peu pendant qu'ils attendent que les choses se chargent.

Nous pourrions utiliser la pseudoclasse :hover pour agir comme un bouton pause. Mais l’utilisateur pourra resynchroniser les deux ensembles de barres en survolant chaque barre, jusqu'à ce qu'elle soit synchronisée avec son acolyte. La première étape consiste à donner des délais aléatoires sur les barres inversées, ce qui peut se faire via la fonction   random()  de Sass :

$cd-bars: #15DEA5;
$cd-bars-inv: #0E397F;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    &__bar {
        animation: bars $anim-dur backwards infinite ease-in-out alternate;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
            &--#{$i}-inv {
                animation-delay: $anim-delay * $i + random(100)*15ms;
                animation-direction: alternate-reverse;
                animation-fill-mode: forwards;
                background-color: $cd-bars-inv;
            }
        }
    }
}

La fonction aléatoire génère un entier compris entre 1 et le nombre choisi comme argument, qui dans notre cas de figure est 100. Nous générons donc un nombre aléatoire entre 1 et 100, puis nous le multiplions par 15 ms et l'ajoutons à notre retard incrémentiel, créant ainsi un animation-delay aléatoire pour chacune des barres inversées :

Barres ondulantes délai aléatoire

Maintenant, tout ce que nous avons à faire est d'ajouter notre bouton pause en assignant à  .load__bar  une pseudoclasse  :hover permettant de régler animation-play-state sur « paused » :

$cd-bars: #15DEA5;
$cd-bars-inv: #0E397F;
$size: 3vh;
$anim-dur: 1000ms;
$anim-delay: $anim-dur / 5;

.load {
    &__bar {
        animation: bars $anim-dur backwards infinite ease-in-out alternate;
        @for $i from 1 through 5 {
            &--#{$i} {
                animation-delay: $anim-delay * $i;
            }
            &--#{$i}-inv {
                animation-delay: $anim-delay * $i + random(100)*15ms;
                animation-direction: alternate-reverse;
                animation-fill-mode: forwards;
                background-color: $cd-bars-inv;
                &:hover {
                    animation-play-state: paused;
                }
            }
        }
    }
}

Pendant que nous y sommes, ajoutons quelques instructions directement dans notre HTML.

<div class="container">
    <span>
        Survolez les barres bleues pour les synchroniser</span>
    <div class="load">
        <div class="load__bar load__bar--1"></div>
        <div class="load__bar load__bar--2"></div>
        <div class="load__bar load__bar--3"></div>
        <div class="load__bar load__bar--4"></div>
        <div class="load__bar load__bar--5"></div>
    </div>
    <div class="load">
        <div class="load__bar load__bar--1-inv"></div>
        <div class="load__bar load__bar--2-inv"></div>
        <div class="load__bar load__bar--3-inv"></div>
        <div class="load__bar load__bar--4-inv"></div>
        <div class="load__bar load__bar--5-inv"></div>
    </div>
    <span>avec leurs équivalents verts au dessus.</span>
</div>

Et c'est tout ! Maintenant, nos barres vont démarrer de façon aléatoire, et c'est l'utilisateur qui pourra les resynchroniser en les survolant avec sa souris :

Barres ondulantes jeu survol souris

Pas mal du tout !

De quoi tenir votre utilisateur alerte pour un petit moment.

Si vous voulez les manipuler directement, jetez un œil à mon CodePen.

Essayez de changer le temps de anim-delay 😀

En résumé

  • nous pouvons répéter un ensemble de keyframes autant de fois que nous le souhaitons en utilisant la propriété  animation-iteration-count, avec le nombre de cycles comme valeur ;

  • nous pouvons régler nos keyframes pour qu'ils se répètent à l'infini en utilisant la propriété  animation-iteration-count  avec le mot clé « infinite » ;

  • la propriété  animation-direction  nous permet de lire un ensemble de keyframes normalement, avec le mot clé « normal » ;

  • la propriété  animation-direction  nous permet de lire un ensemble de keyframes vers l'arrière avec le mot clé « reverse » ;

  • la propriété  animation-direction  nous permet de lire un ensemble de keyframes avec des allers-retours avec le mot clé « alternate » ;

  • et enfin, la propriété  animation-direction  nous permet de lire un ensemble de keyframes avec des allers-retours, mais en commençant par la fin avec le mot clé « alternate-reverse » ;

  • nous pouvons mettre en pause une animation avec keyframe en assignant à la propriété  animation-play-state  la valeur réglée sur « paused » ;

  • nous pouvons reprendre la lecture d'une animation avec keyframe en assignant la propriété  animation-play-state  avec la valeur réglée sur « running ».

Nous avons maintenant fait un tour d’ensemble plutôt utile de l'animation CSS et des propriétés de transition ! Toutes les propriétés que le CSS peut vous offrir sont désormais à votre disposition pour l'animation. Vous avez sûrement hâte de les mettre en pratique pour donner vie à vos pages. Mais avant de conclure, nous allons apprendre à maîtriser un nouvel outil qui nous aidera à construire et peaufiner plus rapidement vos animations en revenant à DevTools. Rendez-vous au chapitre suivant !

Exemple de certificat de réussite
Exemple de certificat de réussite