Décomposition du cercle SVG en chemins
Publié: 2022-03-10Cet article commence par un aveu : j'aime coder manuellement le SVG. Ce n'est pas toujours le cas mais assez souvent cela peut sembler particulier à des personnes qui ne partagent pas ma prédilection. Il y a un bon nombre d'avantages à pouvoir écrire du SVG à la main, comme l'optimisation des SVG d'une manière qu'un outil ne peut pas (transformer un chemin en un chemin ou une forme plus simple), ou en comprenant simplement comment fonctionnent les bibliothèques comme D3 ou Greensock .
Cela dit, j'aimerais examiner de plus près les formes circulaires en SVG et les choses que nous pouvons faire avec elles lorsque nous dépassons un cercle de base. Pourquoi les cercles ? Eh bien, j'adore les cercles. C'est ma forme préférée.
Tout d'abord (j'espère que vous avez déjà vu un cercle de base en SVG), voici un stylo qui en montre un :
Beaucoup de choses peuvent être faites avec un cercle : il peut être animé et on peut lui appliquer différentes couleurs. Pourtant, il y a deux choses très intéressantes que vous ne pouvez pas faire faire par un cercle dans SVG 1.1 : vous ne pouvez pas faire bouger un autre élément graphique le long du chemin du cercle (en utilisant l'élément animateMotion
) et vous ne pouvez pas façonner un texte le long du chemin d'un cercle (cela ne sera autorisé qu'après la sortie de SVG 2.0).
Transformer notre cercle en chemin
Il existe un petit outil en ligne qui peut vous aider à créer des chemins à partir de cercles (vous pouvez l'essayer ici), mais nous allons tout créer à partir de zéro afin de découvrir ce qui se passe réellement dans les coulisses.
Pour faire un chemin circulaire, nous allons en fait faire deux arcs, c'est-à-dire des demi-cercles qui complètent le cercle dans un chemin. Comme vous l'avez probablement remarqué dans le SVG ci-dessus, les attributs CX
, CY
et R
définissent respectivement où le cercle est dessiné le long des axes X et Y, tandis que R
définit le rayon du cercle. Le CX
et le CY
créent le centre du cercle, de sorte que le cercle est dessiné autour de ce point.
La réplication de ce cercle pourrait ressembler à ceci :
<path d=" M (CX - R), CY a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 " />
Notez que CX
est identique à l'attribut cx
du cercle ; il en va de même pour CY
et l'attribut cy
du cercle, ainsi que R
et l'attribut r
du cercle. Le petit a
est utilisé pour définir un segment d'arc elliptique. Vous pouvez utiliser un Z
(ou z
) facultatif pour fermer le chemin.
La lettre minuscule a
indique le début d'un arc elliptique dessiné relativement à la position actuelle — ou dans notre cas spécifique :
<path d=" M 25, 50 a 25,25 0 1,1 50,0 a 25,25 0 1,1 -50,0 " />
Vous pouvez voir la magie se produire dans ce stylo :
Caché sous le chemin se trouve un cercle avec un remplissage rouge. Lorsque vous jouez avec les valeurs du chemin, vous verrez ce cercle tant que le chemin couvre totalement le cercle (le chemin lui-même est un cercle de la même taille), et nous saurons que nous faisons les choses correctement .
Une chose que vous devez également savoir est que tant que vous dessinez des arcs relatifs, vous n'avez pas besoin de répéter la commande a
pour chaque arc que vous dessinez. Lorsque vos 7 premières entrées sont terminées pour votre arc, les 7 secondes entrées seront prises pour l'arc suivant.
Vous pouvez essayer cela avec le stylo ci-dessus en supprimant le deuxième a
dans le chemin :
a 25,25 0 1,1 50,0 25,25 0 1,1 -50,0
Cela peut sembler le même, mais je préfère le laisser jusqu'à ce que je sois prêt à terminer un dessin, et cela m'aide aussi à garder une trace de l'endroit où j'en suis.
Comment fonctionne ce chemin
Tout d'abord, nous nous déplaçons vers une coordonnée X,Y
absolument positionnée dans l'image. Il ne dessine rien là-bas - il se déplace simplement là-bas. Rappelez-vous que pour un élément de cercle CX
, CY
désigne le centre du cercle ; mais comme cela se produit dans l'arc elliptique, les véritables CX
et CY
de l'arc seront calculés à partir des autres propriétés de cet arc.
En d'autres termes, si nous voulons que notre CX
soit à 50
et que notre rayon soit à 25
, alors nous devons passer à 50 - 25
(si nous dessinons de gauche à droite, bien sûr). Cela signifie que notre premier arc est tiré de 25 X, 50 Y
ce qui donne à notre premier arc 25,25 0 1,0 50,0
.
Décomposons ce que signifie réellement la valeur 25,25 0 1,0 50,0
de notre arc :
-
25
: Le rayon X relatif de l'arc ; -
25
: Le rayon Y relatif de l'arc ; -
0 1,0
: Je ne vais pas parler des trois valeurs médianes (rotation, large-arc-flag et les propriétés de balayage) car elles ne sont pas très importantes dans le contexte de l'exemple actuel tant qu'elles sont les mêmes pour les deux arcs ; -
50
: La coordonnée X finale (relative) de l'arc ; -
0
: La coordonnée Y finale (relative) de l'arc.
Le deuxième arc est un 25,25 0 1,0 -50,0
. Gardez à l'esprit que cet arc commencera à dessiner là où le dernier arc a cessé de dessiner. Bien sûr, les rayons X et Y sont les mêmes ( 25
), mais la coordonnée X finale est -50
de l'endroit où se trouve l'actuelle.
Évidemment, ce cercle aurait pu être tracé de différentes manières. Ce processus de transformation d'un cercle en chemin est appelé décomposition. Dans la spécification SVG 2, la décomposition d'un cercle se fera avec 4 arcs, cependant, la méthode qu'elle recommande n'est pas encore utilisable, car elle dépend actuellement d'une fonctionnalité nommée chemin de fermeture de segment qui n'a pas encore été spécifiée.
Afin de vous montrer qu'on peut dessiner le cercle de plein de façons, j'ai préparé un petit stylo avec divers exemples :
Si vous regardez de plus près, vous verrez notre cercle d'origine ainsi que cinq exemples différents de la façon de dessiner des chemins au-dessus de ce cercle. Chaque chemin a un élément enfant desc
décrivant l'utilisation des valeurs CX
, CY
et R
pour construire le cercle. Le premier exemple est celui dont nous avons parlé ici tandis que trois autres utilisent des variantes qui devraient être compréhensibles à la lecture du code ; les derniers exemples utilisent quatre arcs semi-circulaires au lieu de deux, reproduisant quelque peu le processus décrit dans la spécification SVG 2 liée ci-dessus.
Les cercles sont superposés à l'aide de l'indexation z naturelle de SVG consistant à placer les éléments qui viennent plus tard dans le balisage au-dessus de ceux qui viennent plus tôt.
Si vous cliquez sur les chemins circulaires dans le stylo, le premier clic imprimera comment le chemin est structuré sur la console et ajoutera une classe à l'élément afin que vous puissiez voir la couleur de trait de la façon dont le cercle est dessiné (vous pouvez voir que le premier cercle est tracé avec un coin de départ à partir du trait). Le deuxième clic supprimera le cercle afin que vous puissiez interagir avec le cercle ci-dessous.
Chaque cercle a une couleur de remplissage différente ; l'élément de cercle réel est jaune et dira "Vous avez cliqué sur le cercle" à la console chaque fois qu'il est cliqué dessus. Vous pouvez aussi, bien sûr, simplement lire le code car les éléments desc
sont assez simples.
Passer d'un chemin à un cercle
Je suppose que vous avez remarqué que bien qu'il existe de nombreuses façons différentes de dessiner le cercle, les chemins utilisés semblent toujours assez similaires. Souvent, en particulier dans les sorties SVG d'un programme de dessin, les cercles seront représentés par des chemins. Cela est probablement dû à l'optimisation du code du programme graphique ; une fois que vous avez le code pour dessiner un chemin, vous pouvez dessiner n'importe quoi, alors utilisez-le. Cela peut conduire à des SVG quelque peu gonflés sur lesquels il est difficile de raisonner.
Lecture recommandée : " Conseils pour créer et exporter de meilleurs SVG pour le Web" par Sara Soueidan
Prenons le SVG suivant de Wikipedia comme exemple. Lorsque vous regardez le code de ce fichier, vous verrez qu'il a beaucoup de cruauté d'éditeur une fois que vous l'avez exécuté dans SVGOMG de Jake Archibald ! (sur lequel vous pouvez en savoir plus ici), vous vous retrouverez avec quelque chose comme le fichier suivant qui a été assez optimisé mais les cercles dans le document sont toujours rendus sous forme de chemins :
Voyons donc si nous pouvons comprendre ce que ces cercles devraient être s'ils étaient de véritables éléments de cercle compte tenu de ce que nous savons sur le fonctionnement des chemins. Le premier chemin dans le document n'est évidemment pas un cercle alors que les deux suivants le sont (affichant uniquement l'attribut d
) :
M39 20a19 19 0 1 1-38 0 19 19 0 1 1 38 0z
M25 20a5 5 0 1 1-10 0 5 5 0 1 1 10 0z
Donc, en se souvenant que le second a
peut être omis, réécrivons-les pour qu'ils aient un peu plus de sens. (Le premier chemin est le grand cercle.)
M39 20 a19 19 0 1 1-38 0 a19 19 0 1 1 38 0z
Ces arcs sont alors évidemment les suivants :
aR R 0 1 1 - (R * 2) 0 aR R 0 1 1 (R * 2) 0
Cela signifie que notre rayon de cercle est de 19
, mais quelles sont nos valeurs CX
et CY
? Je pense que notre M39
est en fait CX + R
, ce qui signifie que CX
est 20
et CY
est 20
aussi.
Disons que vous ajoutez un cercle après tous les chemins comme ceci :
<circle fill="none" stroke-width="1.99975" stroke="red" r="19" cx="20" cy="20" />
Vous verrez que c'est correct et que le cercle rouge couvre exactement le grand cercle. Le deuxième chemin circulaire reformulé ressemble à ceci :
M25 20 a5 5 0 1 1-10 0 5 5 0 1 1 10 0z
Évidemment, le rayon est de 5
, et je parie que nos valeurs CX
et CY
sont les mêmes qu'avant : - 20
.
Remarque : Si CX = 20
, alors CX + R = 25
. Le cercle est assis à l'intérieur du plus grand au centre, il devrait donc évidemment avoir les mêmes valeurs CX
et CY
.
Ajoutez le cercle suivant à la fin des chemins :
<circle fill="yellow" r="5" cx="20" cy="20" />
Vous pouvez maintenant voir que c'est correct en jetant un œil au stylo suivant :
Maintenant que nous savons ce que devraient être les cercles, nous pouvons supprimer ces chemins inutiles et créer réellement les cercles - comme vous pouvez le voir ici :
Utilisation de notre chemin circulaire pour l'habillage du texte
Alors maintenant que nous avons nos cercles dans des chemins, nous pouvons envelopper du texte sur ces chemins. Ci-dessous se trouve un stylo avec les mêmes chemins que notre précédent stylo "Tous les cercles", mais avec du texte enveloppé sur le chemin. Chaque fois que vous cliquez sur un chemin, ce chemin sera supprimé et le texte sera enveloppé sur le prochain chemin disponible, comme ceci :
En regardant les différents chemins, vous verrez de minuscules différences entre chacun (plus à ce sujet dans un instant), mais il y a d'abord une petite incompatibilité entre navigateurs à voir - particulièrement visible dans le premier chemin :
Développeur Firefox | |
Chrome | |
Bord Microsoft |
La raison pour laquelle le "S" de départ de "Smashing" se trouve à cet angle amusant dans la solution Firefox est que c'est là que nous avons réellement commencé à tracer notre chemin (en raison de la commande vR que nous avons utilisée). Ceci est plus évident dans la version Chrome où vous pouvez clairement voir le premier coin en forme de tarte de notre cercle que nous avons dessiné :
Chrome ne suit pas tous les coins, c'est donc le résultat lorsque vous modifiez le texte en "Smashing Magazine". |
La raison en est que Chrome a un bogue concernant l'héritage de l'attribut textLength
déclaré sur l'élément de text
parent. Si vous voulez qu'ils se ressemblent tous les deux, placez l'attribut textLength
sur l'élément textPath
ainsi que sur le texte. Pourquoi? Car il s'avère que Firefox Developer a le même bug si l'attribut textLength
n'est pas spécifié sur l'élément text
(c'est le cas depuis quelques années maintenant).
Microsoft Edge a un bogue totalement différent ; il ne peut pas gérer les espaces entre le Text
et l'élément TextPath
enfant. Une fois que vous avez supprimé les espaces et placé l'attribut textLength
sur les éléments text
et textPath
, ils auront tous un aspect relativement identique (avec de petites variations dues aux différences dans les polices par défaut, etc.). Donc, trois bogues différents sur trois navigateurs différents — c'est pourquoi les gens préfèrent souvent travailler avec des bibliothèques !
Le stylet suivant montre comment les problèmes peuvent être résolus :
J'ai également supprimé les différentes couleurs de remplissage car cela permet de voir plus facilement l'habillage du texte. Supprimer les couleurs de remplissage signifie que ma petite fonction pour vous permettre de parcourir les chemins et de voir à quoi ils ressemblent ne fonctionnera pas à moins que j'ajoute un attribut pointer-events="all"
, donc je les ai également ajoutés.
Remarque : Vous pouvez en savoir plus sur les raisons de cela dans "Managing SVG Interaction With The Pointer Events Property" expliqué par Tiffany B. Brown.
Nous avons déjà discuté de l'enveloppement du chemin multiarc, alors regardons maintenant les autres. Puisque nous avons un chemin sur lequel nous nous enroulons, le texte se déplacera toujours dans la même direction.
Image | Chemin | Explication |
---|---|---|
M CX, CY a R, R 0 1,0 -(R * 2), 0 a R, R 0 1,0 R * 2, 0 et utilise la fonction translate pour déplacer +R sur l'axe X. | La position de départ de notre textPath (puisque nous ne l'avons spécifié d'aucune façon) est déterminée par notre premier arc de fin -(R * 2) , compte tenu du rayon de l'arc lui-même. | |
M (CX + R), CY a R,R 0 1,0 -(R * 2),0 a R,R 0 1,0 (R * 2),0 | Même chose que le chemin précédent. | |
M CX CY m -R, 0 a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 | Puisque nous terminons à (R * 2 ) dans notre premier arc, nous commencerons évidemment à la position opposée. En d'autres termes, celui-ci commence là où nos deux chemins précédents se sont terminés. | |
M (CX - R), CY a R,R 0 1, 1 (R * 2),0 a R,R 0 1, 1 -(R * 2),0 | Cela commence à la même position que le dernier en raison de (R * 2) , mais il tourne dans le sens des aiguilles d'une montre car nous avons défini la propriété sweep-flag (marquée en jaune) sur 1 . |
Nous avons vu comment envelopper du texte sur un seul chemin dans un cercle. Voyons maintenant comment nous pouvons diviser ce chemin en deux chemins et les avantages que vous pouvez en tirer.
Briser nos chemins en plusieurs parties
Il y a beaucoup de choses que vous pouvez faire avec le texte dans votre chemin, c'est-à-dire obtenir des effets stylistiques avec des éléments tspan
, définir le décalage du texte ou animer le texte. Fondamentalement, tout ce que vous ferez sera limité par le chemin lui-même. Mais en divisant nos chemins multiarcs en chemins d'arc uniques, nous pouvons jouer avec la direction de notre texte, l'indexation z de différentes parties de notre texte et réaliser des animations plus complexes.
Tout d'abord, nous allons vouloir utiliser une autre image SVG pour montrer certains des effets. J'utiliserai le diamant de l'article sur les événements de pointeur que j'ai mentionné plus tôt. Tout d'abord, montrons à quoi cela ressemblera avec un texte circulaire à chemin unique posé dessus.
Supposons que notre cercle est CX 295, CY 200, R 175
. Maintenant, en suivant la méthode du chemin circulaire, nous voyons maintenant ce qui suit :
M (CX - R), CY a R,R 0 1,1 (R * 2),0 a R,R 0 1,1 -(R * 2),0
Je ne vais pas parler du chemin ou de la taille du texte, de la couleur de remplissage ou du contour. Nous devrions tous comprendre cela maintenant et être capables de faire en sorte que ce soit ce que nous voulons qu'il soit. Mais en regardant le texte, on voit tout de suite quelques bémols ou limites :
- Le texte va tous dans une direction;
- Il pourrait être agréable d'avoir une partie du texte derrière l'améthyste, en particulier là où il est écrit MAGAZINE. Afin d'aligner le « M » et le « E » sur le cercle, le « A » doit être sur le point inférieur latéral de l'améthyste, ce qui semble en quelque sorte déséquilibré d'une autre manière. (J'ai l'impression que le 'A' doit être positionné avec précision et pointer vers le bas à ce point.)
Si nous voulons résoudre ces problèmes, nous devons diviser notre chemin unique en deux. Dans le stylo suivant, j'ai séparé le chemin en deux chemins (et les ai placés dans la zone defs
du SVG pour que nos textPath
référencés):
Encore une fois, en supposant que notre CX
est 295, CY 200, R 175
, alors les deux chemins sont au format suivant (pour le chemin semi-circulaire supérieur):
M (CX - R), CY a R,R 0 1,1 (R * 2),0
Et ce qui suit pour le bas :
M (CX + R), CY a R,R 0 1,1 -(R * 2),0
Cependant, nous avons toujours du texte circulaire qui se déplace tous dans la même direction. Pour résoudre ce problème pour tout sauf Edge, tout ce que vous avez à faire est d'ajouter l'attribut side="right"
à l'élément de text
contenant le 'MAGAZINE' textPath
.
Faire aller le texte dans une autre direction
Si nous voulons prendre en charge autant de navigateurs que possible, nous devons modifier le chemin et ne pas compter sur l'attribut side
qui n'est pas entièrement pris en charge. Ce que nous pouvons faire, c'est copier notre chemin de demi-cercle supérieur, mais changer le balayage de 1
à 0
:
Avant de:
M 120, 200 a 175,175 0 1,
M 120, 200 a 175,175 0 1,
1
350,0
350,0
Après:
M 120, 200 a 175,175 0 1,
M 120, 200 a 175,175 0 1,
0
350,0
350,0
Mais notre texte est maintenant dessiné sur le cercle intérieur défini par le balayage et il ne sera pas aussi beau dans différents navigateurs. Cela signifie que nous allons devoir déplacer la position de notre chemin pour l'aligner avec le 'S' de 'Smashing', agrandir le X de fin du chemin et définir un décalage par rapport au texte. Comme vous pouvez le voir, il y a aussi une petite différence de texte entre Firefox et les autres que nous pouvons améliorer en augmentant l'attribut textLength
sur l'élément de text
, ainsi qu'en supprimant les espaces du textPath
(puisque Firefox pense évidemment que les espaces sont significatifs).
La solution:
Modifier l'index Z d'une partie de notre texte circulaire
Enfin, nous voulons que notre texte passe à la fois devant et derrière l'améthyste. Eh bien, c'est facile. Rappelez-vous que l'indexation z des éléments de SVG est basée sur leur emplacement dans le balisage ? Donc si nous avons deux éléments, l'élément 1
sera dessiné derrière l'élément 2
. Ensuite, tout ce que nous avons à faire est de déplacer un élément de text
vers le haut dans notre balisage SVG afin qu'il soit dessiné avant l'améthyste.
Vous pouvez voir le résultat ci-dessous dans lequel des parties du mot 'MAGAZINE' sont masquées par la pointe inférieure de l'améthyste.
Si vous regardez le balisage, vous pouvez voir que le demi-cercle inférieur du texte a été déplacé pour être avant le chemin qui dessine l'améthyste.
Animer les parties de notre cercle
Nous avons donc maintenant la possibilité de créer un texte circulaire en contrôlant complètement la directionnalité des parties de notre texte en mettant le texte en deux demi-cercles. Cela peut, bien sûr, aussi être exploité pour faire des animations du texte. Faire des animations SVG multi-navigateurs est vraiment le sujet d'un autre article (ou de beaucoup plus d' articles). Ces exemples ne fonctionneront que dans Chrome et Firefox en raison de l'utilisation de la syntaxe des animations SMIL au lieu des images clés CSS ou d'outils comme Greensock. Mais cela donne un bon indicateur des effets que vous pouvez obtenir en animant le cercle décomposé.
Prenez le stylo suivant :
Veuillez appuyer sur le bouton 'Rerun' sur le codepen pour voir l'animation en action. Les deux parties de notre texte circulaire commencent à s'animer en même temps, mais ont une durée différente et se terminent donc à des moments différents. Parce que nous animons l'attribut textLength
, nous avons mis deux directives d' animate
sous chaque texte - une pour l'élément text
(pour que Firefox fonctionne) et une pour l'élément textpath
(pour que Chrome fonctionne).
Conclusion
Dans cet article, nous avons vu comment transformer un cercle en chemin et inversement, afin de mieux comprendre quand nous devons optimiser un chemin et quand non. Nous avons vu comment transformer le cercle en chemin nous libère pour placer le texte sur le chemin circulaire, mais aussi comment diviser davantage le chemin circulaire en demi-cercles et obtenir un contrôle plus complet sur la directionnalité et l'animation des composants de notre texte circulaire .
Lectures complémentaires sur SmashingMag :
- Repenser le SVG réactif
- Animer des fichiers SVG avec SVGator
- Styliser et animer des SVG avec CSS
- Gestion de l'interaction SVG avec la propriété Pointer Events