svg : coordonnées et transformations

Dans les articles précédents j’ai présenté le SVG en prenant les valeurs par défaut au niveau du système de coordonnées. Mais on peut évidemment adapter le système selon nos besoins. On va voir ça dans cet article.

On verra aussi les transformations (translation, redimentionnement, rotation…)

Les coordonnées

Viewport

Par défaut l’origine se situe dans le coin supérieur gauche. Voici une représentation extraite de cet article de présentation :

On a un espace en deux dimensions avec l’origine x=0 et y=0 dans le coin supérieur gauche. les valeurs de x et y croissent quand on s’éloigne de cette origine. c’est un système classique dès qu’on dessine sur le web.

Par défaut les valeurs sont en pixels.

On parle de viewport pour désigner l’espace visible d’un dessin. Quand on écrit :

<svg width="600px" height="400px">

On crée un viewport de 600 px de large et 400 px de hauteur.

On peut exprimer ces valeurs autrement qu’avec des pixels (em, ex, pt, pc, mm, un et cm), par exemple des pouces mais au final ça sera converti en pixels. Si on ne précise aucune unité ça sera automatiquement des pixels.

Si on met une valeur en pourcentage on aura la dimension disponible dans le contenant (un svg parent ou la taille de la fenêtre).

ViewBox

En général ces dispositions conviennent mais il arrive des fois qu’on ait besoin d’autres unités que les pixels ou alors que l’origine des axes se situe ailleurs. Dans ce cas on utilise un attribut viewBox. Avec ce viewBox on peut déterminer en particulier quelle sera la dimension des unités.

C’est pas clair ? Bon alors on va prendre un exemple :

<svg width="600px" height="400px" viewBox="0 0 300 200>

Au niveau des valeurs pour le viewBox on définit les valeurs mini (0,0) et les valeurs maxi (300, 200).

Alors bien qu’on ait définit un espace (viewport) de 600*400 px le système d’unités sera de 0 à 300 sur l’axe x et de 0 à 200 sur l’axe y.

On voit que le point (200, 100) est en réalité en (400px, 200px).

Prenons un exemple pratique pour illustrer ça. Voilà un dessin sans viewBox :

<svg width="400" height="400" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <rect fill="#d7de12" height="100%" width="100%"/> 
  <g stroke="#00f" stroke-width="1"> 
    <line x1="0" x2="400" y1="100" y2="100"/> 
    <line x1="0" x2="400" y1="200" y2="200"/> 
    <line x1="0" x2="400" y1="300" y2="300"/> 
    <line x1="100" x2="100" y1="0" y2="400"/> 
    <line x1="200" x2="200" y1="0" y2="400"/> 
    <line x1="300" x2="300" y1="0" y2="400"/> 
  </g> 
  <rect x="100" y="100" width="100" height="100" fill="none" stroke="#a02222" stroke-width="6"/> 
</svg>

 

Maintenant j’ajoute un viewBox pour avoir des valeurs divisées par 2 :

<svg width="400" height="400" version="1.1" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <rect fill="#d7de12" height="100%" width="100%"/> 
  <g stroke="#00f" stroke-width="1"> 
    <line x1="0" x2="200" y1="50" y2="50"/> 
    <line x1="0" x2="200" y1="100" y2="100"/> 
    <line x1="0" x2="200" y1="150" y2="150"/> 
    <line x1="50" x2="50" y1="0" y2="200"/> 
    <line x1="100" x2="100" y1="0" y2="200"/> 
    <line x1="150" x2="150" y1="0" y2="200"/> 
  </g> 
  <rect x="50" y="50" width="50" height="50" fill="none" stroke="#a02222" stroke-width="3"/> 
</svg>

On obtient le même résultat mais maintenant on a utilisé la moitié des valeurs pour les éléments.

Maintenant que se passe-t-il en changeant les valeurs minimales du viewBox ? L’effet est une translation :

<svg width="400" height="400" version="1.1" viewBox="100 100 500 500" xmlns="http://www.w3.org/2000/svg">
  <rect fill="#d7de12" height="100%" width="100%"/> 
  <g stroke="#00f" stroke-width="1"> 
    <line x1="100" x2="500" y1="200" y2="200"/>
    <line x1="100" x2="500" y1="300" y2="300"/>
    <line x1="100" x2="500" y1="400" y2="400"/> 
    <line x1="200" x2="200" y1="100" y2="500"/> 
    <line x1="300" x2="300" y1="100" y2="500"/> 
    <line x1="400" x2="400" y1="100" y2="500"/> 
  </g> 
  <rect x="200" y="200" width="100" height="100" fill="none" stroke="#a02222" stroke-width="6"/> 
</svg>

Avec ce code on obtient le même dessin mais toutes les valeurs sont décalées de 100 px vers la gauche et vers le haut.

Gestion du ratio

Dans mon exemple ci-dessus j’ai fait en sorte que les proportions soient identiques pour le viewport (400*400) et le viewBox (200*200). Mais que se passe-t-il si le rapport n’est pas le même ? Prenons un exemple :

<svg width="400" height="400" version="1.1" viewBox="0 0 100 200" xmlns="http://www.w3.org/2000/svg">
  <rect fill="#d7de12" height="100%" width="100%"/> 
  <g stroke="#00f" stroke-width="1"> 
    <line x1="0" x2="100" y1="50" y2="50"/> 
    <line x1="0" x2="100" y1="100" y2="100"/> 
    <line x1="0" x2="100" y1="150" y2="150"/> 
    <line x1="25" x2="25" y1="0" y2="200"/> 
    <line x1="50" x2="50" y1="0" y2="200"/> 
    <line x1="75" x2="75" y1="0" y2="200"/> 
  </g> 
  <rect x="25" y="50" width="25" height="50" fill="none" stroke="#a02222" stroke-width="3"/> 
</svg>

J’ai un viewport de 400*400 px et un viewBow de 100*200 qui part de l’origine (0,0). J’ai adapté les valeurs en fonction de ce viewport. Que ce passe-t-il au niveau de l’image avec ce ratio différent ?

On voit que maintenant notre image s’est rétrécie de moitié horizontalement. Est-ce logique ? Oui et non selon le point de vue. Si c’est l’effet désiré alors pas de souci, sinon on peut s’en sortir avec la propriété preserveAspectRatio. Si j’ajoute :

preserveAspectRatio="none"

Voilà le résultat :

Cette fois le ratio n’est pas imposé et l’image occupe toute la surface disponible, mais du coup les valeurs horizontales sont doublées…

On dispose de plusieurs possibilités pour renseigner cette propriété preserveAspectRatio :

  • xMinYMin
  • xMinYMid
  • xMinYMax
  • xMidYMin
  • xMidYMid (valeur par défaut)
  • xMidYMax
  • xMaxYMin
  • xMaxYMid
  • xMaxYMax

On peut ajouter la valeur slice (la valeur par défaut est meet) pour que le viewBox couvre tout le viewport.

Comme c’est assez laborieux d’illustrer les effets de ces déplacement je vous propose plutôt de regarder cette superbe démo en ligne.

Mais bon… pourquoi ne pas garder le ratio ? Il faut vraiment une bonne motivation…

Les transformations

Voyons à présent comment effectuer des transformations : translations, rotations et changement des dimensions.

Translation

Une façon simple d’effectuer une translation est d’utiliser la balise use qu’on a déjà vue dans le premier article. Soit on a une définition, soit déjà une forme et on en crée une autre à des coordonnées différentes. Voilà un exemple :

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="0" y="0" width="100%" height="100%" style="fill: yellow;"/>
  <g id="carre">
    <rect width="50" height="50" style="fill: blue;"/>
  </g>
  <use xlink:href="#carre" x="100" y="100"/>
</svg>

On dessine un premier carré en haut à gauche, on l’équipe d’un identifiant, et on dessine le même de façon décalée. J’ai ajouté une flèche à la main pour figurer la translation.

Maintenant on va changer juste cette ligne :

<use xlink:href="#carre" transform="translate(100,100)"/>

Au lieu de préciser les coordonnées de départ du carré on effectue une translation complète du système de coordonnées de telle façon que l’origine se retrouve au point (100,100). Du coup le nouveau carré est tracé au même endroit.

Vous allez dire mais à quoi bon cette translation ? C’est vrai que dans mon exemple c’est beaucoup de mouvement pour pas grand chose, comme si on déplaçait une maison pour bouger un meuble ! Mais on va bientôt voir que ça peut devenir très utile !

Dimensions

On peut aussi jouer sur les dimensions avec scale(valeur) :

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="0" y="0" width="100%" height="100%" style="fill: yellow;"/>
  <g id="carre">
    <rect x="50" y="50" width="50" height="50" style="fill: blue;"/>
  </g>
  <use xlink:href="#carre" transform="scale(2)"/>
</svg>

Le résultat vous surprend ? Bon, le second carré a doublé ses dimensions comme prévu mais pourquoi cette translation ?

Lorsqu’on ajoute scale(2) on change le système de coordonnées en doublant ses valeurs. Comme le point de départ du carré est en (50, 50) et que les valeurs sont doublées alors le départ se retrouve en (100, 100), ce qu’on voit bien sur la figure.

On peut différencier x et y :

<use xlink:href="#carre" transform="scale(1.5, 2)"/>

Suite de transformations

On n’est pas limité à une seule transformation sur un objet :

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="0" y="0" width="100%" height="100%" style="fill: yellow;"/>
  <g id="carre">
    <rect width="50" height="50" style="fill: blue;"/>
  </g>
  <use xlink:href="#carre" transform="translate(100,100) scale(2, 2)"/>
</svg>

Là on fait une translation et un changement des dimensions.

Rotation

On peut aussi appliquer une rotation avec rotate(valeur en degrés) :

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="0" y="0" width="100%" height="100%" style="fill: yellow;"/>
  <defs>
    <rect id="carre" width="50" height="50"/>
  </defs>
  <g transform="translate(100, 100)">
    <use xlink:href="#carre" style="fill: red;" transform="rotate(90)"/>
    <use xlink:href="#carre" style="fill: magenta;" transform="rotate(180)"/>
    <use xlink:href="#carre" style="fill: crimson;" transform="rotate(-90)"/>
    <use xlink:href="#carre" style="fill: olive;"/>    
    <use xlink:href="#carre" style="fill: blue;" transform="rotate(45)"/>    
    <use xlink:href="#carre" style="fill: green;" transform="rotate(135)"/>
    <use xlink:href="#carre" style="fill: orange;" transform="rotate(-135)"/>
    <use xlink:href="#carre" style="fill: indigo;" transform="rotate(-45)"/>
  </g>
</svg>

J’ai commencé par une translation pour situer le centre de rotation au milieu. Une autre façon de procéder est d’ajouter les coordonnées du centre de rotation comme second et troisième paramètre :

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="0" y="0" width="100%" height="100%" style="fill: yellow;"/>
  <defs>
    <rect x="100" y="100" id="carre" width="50" height="50"/>
  </defs>
  <use xlink:href="#carre" style="fill: red;" transform="rotate(90,100,100)"/>
  <use xlink:href="#carre" style="fill: magenta;" transform="rotate(180,100,100)"/>
  <use xlink:href="#carre" style="fill: crimson;" transform="rotate(-90,100,100)"/>
  <use xlink:href="#carre" style="fill: olive;"/>    
  <use xlink:href="#carre" style="fill: blue;" transform="rotate(45,100,100)"/>    
  <use xlink:href="#carre" style="fill: green;" transform="rotate(135,100,100)"/>
  <use xlink:href="#carre" style="fill: orange;" transform="rotate(-135,100,100)"/>
  <use xlink:href="#carre" style="fill: indigo;" transform="rotate(-45,100,100)"/>
</svg>

Avec évidemment le même résultat !

Avec un léger changement de code on peut modifier complètement le résultat :

<rect id="carre" width="60" height="60"/>

Inclinaison (skew)

Une dernière transformation disponible : on peut incliner sur un axe d’un certain angle avec skewX(valeur) et skewY(valeur) :

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="0" y="0" width="100%" height="100%" style="fill: yellow;"/>
  <rect x="50" y="50" width="100" height="100" fill="blue" transform="skewX(10)"/>
</svg>

En utilisant les deux :

<rect x="50" y="50" width="100" height="100" fill="blue" transform="skewX(10) skewY(10)"/>

Conclusion

On a vu dans cet article comment gérer le système de coordonnées et comment effectuer des transformations.

Laisser un commentaire