SVG : Comprendre le système de coordonnées

Actualité ekino -

Article paru le :
Article paru le :

Selon le site can I use, SVG est maintenant reconnu par plus de 97,25% des navigateurs présents sur le marché. Il n’y a donc plus de raison de ne pas adopter ce format. Si par malheur vous travaillez sur un projet qui ne prendrait pas en compte cette statistique, et êtes forcé de supporter IE8 pour ne pas le citer, un fallback en png peut facilement être mis en place.

Cela étant dit, cet article va se concentrer sur le système de coordonnées et les paramètres permettant de le modifier.

Plus précisément, on va s’intéresser au « S » de SVG, pour scalable, soit adaptable en français.

Introduction

La balise svg crée une zone de dessin aux dimensions illimitées, c’est ce que l’on appelle le canvas, dont l’utilisateur ne verra qu’une partie, un rectangle délimité par les attributs width et height de la balise. C’est ce rectangle que l’on appelle le viewport.
Il faut voir ça comme un écran de télévision, celui-ci délimitant une zone d’affichage convenue, alors qu’un plateau de télévision est bien sur plus vaste.
Sans plus d’informations fournies par divers attributs, un système de coordonnées est mis en place, avec pour origine le coin supérieur gauche du viewport.

Maintenant, on va voir comment jouer avec ces coordonnées.

L’attribut viewbox en SVG

Pas de teasing, tout va se faire grâce à l’attribut viewbox. Si aucune valeur n’est précisée, le viewport ne sera pas modifié, et donc le système de coordonnées ne changera pas. Viewbox prend en valeur 4 paramètres :

viewBox = " <min-x> <min-y> <width> <height> "

Ces 4 paramètres n’ont pas d’unités et vont permettre de définir un rectangle qui va redéfinir le système de coordonnées du viewport. Et c’est là que les choses deviennent intéressantes. L’échelle (scale) pourra être modifiée et être supérieure ou inférieure à la valeur de base 1:1, mais toujours de façon à faire rentrer, ou plutôt à contraindre, le viewbox dans le viewport.

Petit aparté visuel sur les bordures des exemples qui vont suivre, pour mieux comprendre mes propos :

  • Le cadre gris foncé représente les limites du svg.
  • Le cadre jaune représente lui le viewbox, c’est le contour d’un rectangle qui fait toujours les dimensions du viewbox. Son contour est de 6px, 3 à l’extérieur, et 3 à l’intérieur de l’élément rect.
  • Par ailleurs, un lien vers codepen est présent sur chacune des images pour permettre de vraiment visualiser le résultat en svg.

Modification de l’échelle de façon homothétique

svg1
<svg width="150" height="150" viewbox="0 0 150 150" />

Pas de surprise, c’est l’exemple de base, le viewbox est précisé et fait les mêmes dimensions que le viewport. Le rond bleu a bien un diamètre de 100, et le rond rose n’est pas visible.

svg2
<svg width="150" height="150" viewbox="0 0 300 300" />

Cette fois le viewbox représente deux fois le viewport. Il en résulte donc une surface d’affichage deux fois fois plus large, mais toujours dans le même viewport. L’échelle est donc inférieure, ici 1:2, les cercles apparaissent deux fois plus petits, et le rose est cette fois visible.

svg3
<svg width="150" height="150" viewbox="0 0 100 100" />

Cette fois-ci, le viewbox est inférieur au viewport. L’échelle est aussi modifiée à 3:2, le rond bleu prend cette fois toute la place disponible, soit un diamètre de 100 * 3 / 2 = 150, ce qui correspond bien aux largeur et hauteur du viewport.

Ces trois exemples ont été faits en précisant un viewbox homothétique au viewport, maintenant, essayons en changeant cela.

Modification de l’échelle de façon non homothétique

svg4
<svg width="150" height="150" viewbox="0 0 150 100" />

L’échelle sur l’axe x n’est pas modifiée, puisque la largeur du viewbox est la même que celle du viewport.
Ce qui est intéressant ici, c’est que l’échelle sur l’axe y n’est pas modifiée non plus, puisque le rond n’est pas écrasé. Le ratio est préservé. Nous verrons plus tard que l’attribut preserveAspectRatio ermet de jouer avec cela.
Une dernière chose bien visible est que le viewport se retrouve centré verticalement dans le viewport. PreserveAspectRatio aura à nouveau son mot à dire sur cette caractéristique.

svg5
<svg width="150" height="150" viewbox="0 0 200 300" />

Sur cet exemple, l’échelle a été modifiée, puisque ni la largeur, ni la hauteur du viewbox ne correspondait au viewport. Par contre, le ratio entre l’axe x et y reste le même, le rond n’est toujours pas déformé, et on peut se rendre compte que cette fois, le viewbox est centré horizontalement dans le viewport.

La translation

Jusqu’à maintenant, on n’avait pas modifié min-x et min-y. Et bien c’est parti!

svg6
<svg width="150" height="150" viewbox="50 -50 150 150" />

Préciser une valeur autre que 0 à min-x et min-y entraine un décalage du viewbox. Plus précisément, une translation (translate) va faire concorder les coordonnées x = 50 et y = -50 du viewbox avec le point d’origine du viewport. Dans notre exemple, il en résulte donc un translate(-50, 50).
À noter que sur cet exemple, le viewbox est identique au viewport, et seule une translation est effectuée.

svg7
<svg width="150" height="150" viewbox="50 50 300 300" />

Dans ce cas, et comme déjà vu un peu plus haut, la zone d’affichage est deux fois plus grande, vu que l’échelle est devenue plus petite. La translation est également là, mais paraît deux fois moins importante. Visuellement, c’est le cas.
Ce qui se passe, c’est que l’échelle est modifiée en premier, et la translation s’effectue sur cette nouvelle échelle. Visuellement, on voit bien un décalage de 25 pixels correspondant aux informations renseignées dans l’attribut viewbox.

svg8
<svg width="150" height="150" viewbox="75 0 150 300" />

Pour ce dernier exemple, je vais cumuler ce que l’on a vu jusqu’à maintenant. La hauteur du viewbox est deux fois celle du viewport, donc l’échelle est inférieure. En revanche, la largeur du viewbox est la même que celle du viewport, et comme l’attribut preserveAspectRatio n’est pas précisé, le ratio entre l’axe x et l’axe y est préservé. Le viewbox fait donc visuellement 75 pixels de large et est centré dans le viewport. Ensuite, il y a une translation de 75 sur l’échelle du viewbox, soit visuellement de 37,5. Le point d’origine de la viewbox se retrouve donc maintenant confondu avec celui du viewport

Ce dernier exemple utilise une translation pour empêcher de centrer le viewbox dans le viewport. On va voir qu’il y a plus simple pour arriver au même résultat.

PreserveAspectRatio

Cet attribut prend deux valeurs en paramètre (le deuxième est facultatif) et n’est pas interprété si l’attribut viewbox est absent :

preserveAspectRatio = " <align> [<meetOrSlice>] "

Le paramètre align

<align> peut prendre comme valeur l’une des dix suivantes : none, xMinYMin, xMinYMid, xMinYMax, xMidYMin, xMidYMid, xMidYMax , xMaxYMin, xMaxYMid, xMaxYMax. À part none, ces valeurs vont permettre d’aligner le viewbox dans le viewport.

none svg9
<svg width="150" height="150" viewbox="0 0 300 150" preserveAspectRatio="none" />

none est la seule valeur un peu spéciale. Dans cet unique cas, le ratio n’est pas préservé et le viewbox vient s’inscrire parfaitement dans le viewport, au risque de déformer les dessins.
À noter que dans ce cas, la valeur de <meetOrSlice> n’est pas prise en compte.

xMinYMin svg10
<svg width="150" height="150" viewbox="0 0 300 150" preserveAspectRatio="xMinYMin" />

Dans cet exemple, nous avons défini un viewbox deux fois plus large que haut. Sans preserveAspectRatio, le viewbox serait centré verticalement dans le viewport, comme on l’a vu précédemment. Mais ici, en précisant comme valeur d’alignement xMinYMin, on force l’alignement du viewbox en haut à gauche.
À noter que les valeurs xMidYMin et xMaxYMin auraient le même résultat visuellement, puisque le viewbox prend toute la valeur du viewport.

xMaxYMin svg11
<svg width="150" height="150" viewbox="0 0 150 300" preserveAspectRatio="xMaxYMin" />

Cette fois, c’est la hauteur qui est supérieure à la largeur. On pourrait s’attendre à ce que le viewbox soit centré horizontalement, mais la valeur xMaxYMin va l’aligner à droite.
De la même façon, xMaxYMid et xMaxYMax ont le même résultat visuel.

xMidYMax svg12
<svg width="150" height="150" viewbox="0 0 150 300" preserveAspectRatio="xMidYMax" />

En reprenant l’exemple précédent, mais en précisant un preserveAspectRatio à xMidYMax, le viewbox est centré horizontalement. xMidYMin et xMidYMid produiront le même effet.
Sur ce cas, on pourrait bien sûr carrément enlever l’attribut, car comme vu sur les exemples de la partie viewbox, avoir ce dernier centré est le comportement par défaut.

Pas la peine d’illustrer tous les cas de figure, les exemples sont suffisamment parlants concernant l’alignement en « Min » ou en « Max ». Par contre, l’intérêt de « Mid » est plutôt limité pour le moment.

Mais c’est là que <meetOrSlice> va rentrer en jeu.

Le paramètre meetOrSlice

<meetOrSlice>, comme son nom l’indique peut avoir deux valeurs différentes : meet , qui est la valeur par défaut, ou slice, la valeur à laquelle nous allons nous intéresser.

Jusqu’à maintenant, on a vu que le viewbox était contraint dans le viewport par la plus grande valeur entre le <width> et le <height> de l’attribut viewbox. slice permet de changer cela et d’utiliser la plus petite valeur.
Si le viewbox n’est pas homothétique au viewport, une partie du viewbox ne sera donc pas visible.

xMinYMin slice svg13
<svg width="150" height="150" viewbox="0 0 300 150" preserveAspectRatio="xMinYMin slice" />

Dans ce cas, le viewbox est plus long que le viewport, et le paramètre xMinYMin permet d’aligner à gauche le viewbox.
Cette fois, c’est xMinYMid et xMinYMax qui ont le même comportement.

xMaxYMin slice svg14
<svg width="150" height="150" viewbox="0 0 300 150" preserveAspectRatio="xMaxYMin slice" />

xMaxYMin permet, à l’inverse de l’exemple précédent, d’aligner à droite le viewbox dans le viewport.
xMaxYMid et xMaxYMax auront le même comportement.

xMidYMin slice svg15
<svg width="150" height="150" viewbox="0 0 300 150" preserveAspectRatio="xMidYMin slice" />

Troisième cas de figure, xMidYMin va permettre de centrer le viewbox horizontalement, car celui-ci est plus long que le viewport.
De la même façon, xMidYMid et xMidYMax auront visuellement le même résultat.

xMidYMid slice svg16
<svg width="150" height="150" viewbox="0 0 150 300" preserveAspectRatio="xMidYMid slice" />

Dernier exemple : cette fois, le viewbox a une hauteur supérieure au viewport.
Pas besoin de reprendre tous les cas de figure possibles, c’est la deuxième partie du paramètre <align> qui sera pris en compte.
xMidYMid va donc permettre de centrer verticalement le viewbox dans le viewport.

Conclusion

Ça y est, nous voilà à la fin de cet article.
Bien comprendre les coordonnées est important et permet de plus facilement travailler avec les SVGs exportés par différents logiciels. Cela permet aussi de résoudre des problématiques simples comme :

  • Mon SVG est bien présent dans la page, mais rien ne s’affiche.
  • Je n’ai qu’une partie du SVG d’affichée, et en plus celle-ci à l’air d’être zoomée.

Un peu de lecture complémentaire pour finir :