Fermer

octobre 19, 2023

Comment animer le long d’un chemin en CSS

Comment animer le long d’un chemin en CSS


Parlons des indicateurs de progrès – ou des chargeurs. C’est vrai qu’il y a tellement de tutoriels à leur sujet et encore plus d’exemples flottant autour de CodePen. Il fut un temps, il y a quelques années à peine, où les chargeurs semblaient être l’exemple incontournable pour la documentation du framework, à côté des applications à faire.

J’ai récemment eu pour tâche de créer l’état de chargement d’un projet, alors naturellement, je me suis tourné vers CodePen pour m’inspirer. Ce que je voulais, c’était une forme circulaire, et les exemples ne manquent pas. Dans de nombreux cas, l’approche consiste en une combinaison d’utilisation du CSS border-radius propriété d’obtenir une forme circulaire et @keyframes pour le faire tourner 0deg à 360deg.

Il me fallait un peu plus que ça. Plus précisément, j’avais besoin d’une forme de beignet qui remplisse l’indicateur de progression au fur et à mesure qu’il passe de 0% à 100%. Heureusement, j’ai trouvé d’excellents exemples de beignets que je pourrais utiliser comme source d’inspiration et plusieurs approches différentes. Par exemple, je pourrais utiliser le « truc » d’un SVG avec un trait qui anime avec une combinaison de stroke-dasharray et stroke-dashoffset. Accompagner Afif a des centaines d’exemples qui utilisent une combinaison de dégradés et de masques CSS.

Il me fallait encore plus. Ce que je voulais vraiment, c’était un indicateur de progression du beignet qui non seulement se remplit à mesure que la progression augmente, mais définit un visuel qui évolue avec la progression. En d’autres termes, je voulais donner l’impression qu’un objet se déplace autour du beignet, laissant derrière lui une trace de progression.

Voir le stylo [Circular animation with offset Pt. 1 [forked]](https://codepen.io/smashingmag/pen/vYvrwXo) par Sam Preethi.

Voir le stylo Animation circulaire avec décalage Pt. 1 [forked] par Sam Preethi.

Regarde ça? Le scooter a une piste circulaire qui se remplit d’un dégradé lorsqu’il se déplace autour de la forme. Si vous utilisez Firefox, vous rencontrerez probablement des problèmes avec la démo car elle repose sur un @property que Firefox ne prend pas en charge encore. Cependant, il est pris en charge dans la version Nightly, nous aurons donc peut-être un support complet à espérer bientôt.

En fin de compte, j’ai fini par combiner plusieurs des techniques que j’ai trouvées et quelques considérations supplémentaires. J’ai pensé partager cette approche parce que j’aime démontrer comment diverses idées peuvent s’assembler pour créer quelque chose de différent. Cette démo utilise des propriétés personnalisées animées, un dégradé conique, CSS offset, et emoji pour produire l’effet. La vérité est que vous pouvez trouver une combinaison ou un ensemble de techniques différentes qui permettent de faire le travail ou de mieux répondre à vos besoins. Il s’agit plutôt d’un exercice de réflexion.

Créer le beignet

Les cercles en CSS sont assez simples. Nous pourrions en dessiner un en SVG et oublier complètement le CSS. C’est une approche valable, mais je suis à l’aise de travailler directement en CSS pour ce genre de chose. Nous commençons par un seul élément dans le HTML :

<div class="progress-circle"></div>

À partir de là, nous définissons les dimensions du cercle. Cela peut être fait en déclarant un width et en utilisant un aspect-ratio pour conserver une forme parfaite.

.progress-circle {
  width: 200px; 
  aspect-ratio: 1;
}

Nous pouvons maintenant arrondir la forme avec le border-radius propriété:

.progress-circle {
  width: 200px; 
  aspect-ratio: 1;
  border-radius: 50%;
}

C’est notre forme ! Bien sûr, nous ne verrons rien pour l’instant, car nous ne l’avons pas rempli de couleur. Faisons-le maintenant avec un conic-gradient. Nous en voulons un car le dégradé se déplace dans une direction circulaire par défaut, en commençant à 0% et boucler un cercle complet à 360deg.

.progress-circle {
  width: 200px; 
  aspect-ratio: 1;
  border-radius: 50%;
  background: conic-gradient(red 10%, #eee 0); 
}

Jusqu’ici, tout va bien:

Voir le stylo [Conic Gradient Circle [forked]](https://codepen.io/smashingmag/pen/zYyaera) par Geoff Graham.

Voir le stylo Cercle de dégradé conique [forked] par Geoff Graham.

Ce que nous regardons est à peu près un diagramme circulaire, n’est-ce pas ? Nous avons créé une forme circulaire et l’avons remplie d’un dégradé conique qui commence par le rouge et s’arrête à une couleur dure. #eeeen remplissant le reste de la tarte d’un gris clair.

La tarte est délicieuse, mais nous visons un beignet, et les beignets ont un trou découpé au centre. Dans le véritable esprit du CSS, il existe différentes manières d’aborder cela. Encore, Temani a démontré à maintes reprises comment les masques CSS peuvent faire des découpes. C’est également une approche claire, car nous pouvons réutiliser le même dégradé conique pour couper un cercle à partir du centre, en modifiant uniquement les valeurs de couleur pour masquer la partie que nous voulons masquer.

J’ai emprunté un chemin différent, en partie pour plus de commodité et en partie pour démontrer comment CSS est capable d’aborder les défis de plusieurs manières. Ainsi, vous pourriez même vous retrouver à emprunter un itinéraire différent de celui que nous démontrons ici. Mon approche consiste à utiliser le ::before pseudo-élément du .progress-circle. Nous le plaçons au-dessus du dégradé conique avec un positionnement absolu, le remplissons d’une couleur unie et le dimensionnons pour qu’il éclipse une partie de la forme principale. Il s’agit essentiellement d’un petit cercle de couleur unie au-dessus d’un plus grand cercle rempli de dégradé.

.progress-circle {
    /* previous styles */
    position: relative;
}
.progress-circle::before {
  content: '';
  position: absolute;
  inset: 20px; 
  border-radius: inherit;
  background: white;
}

Remarquez ce que nous faisons pour positionner le petit cercle. Puisque nous travaillons avec ::beforenous avons besoin du CSS content propriété pour la faire afficher, même avec une valeur vide. À partir de là, nous utilisons le positionnement absolu, en plaçant le plus petit cercle vers le centre avec un inset appliqué dans toutes les directions. Nous sommes capables de inherit le plus grand cercle border-radius avant de définir une couleur d’arrière-plan unie. Nous ne pouvons pas oublier de définir le positionnement relatif sur le plus grand cercle pour (a) définir un contexte d’empilement et (b) maintenir le plus petit cercle dans les limites du plus grand cercle.

Voir le stylo [conic-gradient() [forked]](https://codepen.io/smashingmag/pen/GRPGzjg) par Sam Preethi.

Voir le stylo dégradé conique() [forked] par Sam Preethi.

C’est tout pour le beignet ! Nous l’avons réalisé uniquement en CSS, en nous appuyant sur une combinaison des border-radius propriété, un conic-gradientet un bien positionné ::before pseudo-élémentement.

Animer le progrès

Avez-vous travaillé avec des propriétés CSS personnalisées ? Je ne parle pas simplement de définir --some-variable avec une valeur, mais en utilisant @property pour enregistrer une propriété avec une syntaxe personnalisée. C’est magique de voir comment cela nous permet d’interpoler entre des valeurs que nous ne pouvons normalement pas faire, telles que les valeurs de couleur et d’angle dans les dégradés.

Lorsque nous enregistrons une propriété personnalisée CSS, nous devons mentionner de quoi il s’agit. taper est, par exemple, si la valeur est une <length>, <number>, <color> ou l’un des 11 autres types pris en charge au moment où j’écris ceci. De cette façon, le navigateur comprend avec quel type de valeur il travaille et, le moment venu, il peut mettre à jour la valeur de la variable pour une animation.

Je vais enregistrer une propriété personnalisée appelée --pqui est l’abréviation de sa syntaxe, <percentage>avec une valeur initiale de 10% ce sera le point de « départ » de l’indicateur de progrès.

@property --p {
  syntax: '';
  inherits: false;
  initial-value: 10%;
}

Maintenant, nous pouvons utiliser le --p variable là où nous en avons besoin, par exemple là où la couleur dure s’arrête entre red et #eee dans le dégradé conique du plus grand cercle que nous utilisons comme point de départ.

.progress-circle {
    /* previous styles */ 
    background: conic-gradient(red var(--p), #eee 0); 
}

Nous voulons passer de la valeur initiale de la propriété personnalisée, 10%, à un pourcentage plus élevé afin de déplacer l’arrêt de couleur dure du dégradé autour de la forme. Alors, configurons un CSS transition qui est conçu pour mettre à jour la valeur de --p.

.progress-circle {
  /* previous styles */ 
  background: conic-gradient(red var(--p), #eee 0); 
  transition: --p 2s linear;
}

Nous allons mettre à jour la valeur au survol, en passant de 10% à 80%:

.progress-circle:hover{
  --p: 80%;
}

Encore un petit ajustement : j’aime mettre à jour le cursor en survol afin qu’il soit plus clair à quel type d’interaction l’utilisateur est confronté. Dans ce cas, nous travaillons avec quelque chose qui indique la progression, c’est ainsi que nous allons le configurer :

.progress-circle {
  /* previous styles */
  cursor: progress;
}

Voir le stylo [conic-gradient() animation [forked]](https://codepen.io/smashingmag/pen/mdaKvBe) par Sam Preethi.

Voir le stylo animation conique-gradient() [forked] par Sam Preethi.

Notre cercle est terminé ! Nous pouvons maintenant survoler l’élément et la couleur dure du dégradé conique arrête les transitions de 10% à 80% derrière le petit cercle qui cache le reste du dégradé pour impliquer une forme de beignet. Nous avons enregistré une coutume @property avec une valeur initiale, je l’ai appliqué au dégradé et j’ai mis à jour la valeur en survol.

Se déplacer dans le cercle

La dernière partie de cet exercice consiste à travailler sur l’indicateur de progression. Nous utilisons le dégradé pour indiquer la progression, mais je souhaite bénéficier de l’aide visuelle supplémentaire d’un objet qui se déplace autour du plus grand cercle avec le dégradé lors de la transition des valeurs.

L’idée que j’ai eue était un petit scooter qui semble laisser une trace en pente derrière lui. Nous avons déjà le cercle et la pente, donc tout ce dont nous avons besoin est le scooter et un moyen de lui faire utiliser le plus grand cercle comme piste pour se déplacer.

Voir le stylo [CSS offset animation [forked]](https://codepen.io/smashingmag/pen/NWezoyg) par Sam Preethi.

Voir le stylo Animation de décalage CSS [forked] par Sam Preethi.

Allons-y et ajoutons le scooter au HTML sous forme d’emoji :

<div class="progress-circle">
  <div class="progress-indicator">🛵</div>
</div> 

Si nous avions décidé de créer la forme initiale du beignet avec SVG, nous aurions alors pu utiliser le même chemin que celui utilisé pour le plus grand cercle comme piste. Cependant, nous pouvons toujours obtenir les mêmes pouvoirs de création de chemin en CSS en utilisant le offset-path propriété. C’est tellement comme écrire du SVG en CSS que nous pouvons utiliser exactement les mêmes coordonnées pour un cercle SVG dans le path():

.chart-indicator {
  /* previous styles */
  offset: path("M 100, 0 a 100 100 0 1 1 -.1 0 z");
}

Les coordonnées du chemin SVG sont difficiles à lire, mais voici ce que nous faisons dans ce chemin particulier :

  1. M 100, 0: Ce se déplace la position du point de départ sur un système de coordonnées XY, où 100 est le long de l’axe X et est égal au rayon du plus grand cercle, ou à la moitié de sa largeur, 200px. Le point de départ est fixé à 0 sur l’axe Y, en le plaçant en haut de la forme. Nous commençons donc par le centre supérieur du plus grand cercle.
  2. a 100 100: Cela définit un arc avec des rayons horizontaux et verticaux de 100, nous donnant un nouveau cercle. Même si techniquement nous ne verrons pas le cercle, il y est dessiné, fournissant au scooter une trace invisible qui suit la forme du cercle plus grand.

Encore une chose ! Nous avons un point de départ pour le scooter, grâce aux coordonnées dans le offset-path. Le CSS offset-distance La propriété nous permet de définir le point final où nous prévoyons de décaler le scooter, qui est exactement égal au --p propriété personnalisée.

.chart-indicator {
  /* previous styles */
  offset-path: path("M 100, 0 a 100 100 0 1 1 -.1 0 z");
  offset-distance: var(--p);
}

Nous mettons déjà à jour notre coutume --p propriété en survol pour aider à déplacer la position d’arrêt du dégradé conique à partir d’une valeur initiale de 10% à 80%. Il faudrait faire pareil pour le scooter pour qu’ils bougent ensemble.

.progress-circle:hover > .progress-indicator { --p: 80%; }

J’utilise le combinateur enfant (>) puisque l’indicateur est un enfant direct du cercle. Si votre conception inclut des éléments supplémentaires ou nécessite que le scooter soit un descendant ultérieur, vous pouvez alors envisager un sélecteur de descendant général.

Le résultat final

Voici tout ce que nous avons couvert dans un seul extrait CSS. J’ai un peu nettoyé les choses, comme la configuration de variables pour des valeurs récurrentes, comme le --size du cercle.

/* Custom property */
@property --p {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 10%;
}

/* Large circle */
.progress-circle {
  --size: 200px; 
  --p: 10%; /* fallback for no @property support */

  background: conic-gradient(red calc(-60% + var(--p)), rgb(224, 187, 77) var(--p), #eee 0);
  border-radius: 50%;
  position: relative;
  margin: auto;
  cursor: progress;
}

/* Small circle pseudo-element */
.progress-circle::before {
  content:'Going ten to eighty percent';
  position: absolute;
  inset: 20px; 
  text-align: center;
  padding: 50px;
  font: italic 9pt 'Enriqueta';
  border-radius: inherit;
  background: white;
}

/* The scooter track */
.progress-indicator {
    --size: min-content; 
    offset: path("M 100,0 a 100 100 0 1 1 -.1 0 z");
    offset-distance: var(--p);
    font: 43pt serif;
    transform: rotateY(180deg) translateX(-6px);
}

/* Update initial value on :hover */
.progress-circle:hover,
.progress-circle:hover > .progress-indicator { 
  --p: 80%;
}

/* Controls the width of larger circle and scooter track */
.progress-circle,
.progress-indicator {
    width: var(--size);
    transition: --p 2s linear;
}

Voir le stylo [Circular animation with offset Pt. 1 [forked]](https://codepen.io/smashingmag/pen/vYvrwXo) par Sam Preethi.

Voir le stylo Animation circulaire avec décalage Pt. 1 [forked] par Sam Preethi.

Un scooter et une pente solide ne sont qu’une idée. Que diriez-vous de différents objets avec différents sentiers ?

Voir le stylo [Circular animation with offset Pt. 2 [forked]](https://codepen.io/smashingmag/pen/gOZKJma) par Sam Preethi.

Voir le stylo Animation circulaire avec décalage Pt. 2 [forked] par Sam Preethi.

J’ai fait référence à ce composant à la fois comme un « indicateur de progression » et un « chargeur » tout au long de l’article. Il existe une différence entre l’affichage de la progression et l’état de chargement, mais il est également possible qu’un état de chargement affiche la progression du chargement. C’est pourquoi j’utilise un générique <div> comme un <figure> dans l’exemple, mais vous pourriez tout aussi bien l’utiliser sur des éléments HTML plus sémantiques, comme <progress> ou <meter> en fonction de votre cas d’utilisation spécifique. Pour des raisons d’accessibilité, vous pouvez envisager d’incorporer un texte descriptif qui peut être annoncé sous forme de phrases conviviales pour les technologies d’assistance décrivant les données.

Faites-moi savoir si vous l’utilisez sur un projet et comment vous l’abordez. Partagez-le avec moi dans les commentaires et nous pourrons comparer les notes.

Lectures complémentaires sur SmashingMag

Éditorial fracassant
(jj, ok, il)




Source link

octobre 19, 2023