Accueil
Mes tutoriels sur la programmation

Dessiner avec SVG - sommaire


Vous pouvez me contacter via Facebook (questions, critiques constructives) : page Facebook relative à mon site

SVG : dégradés, motifs, masques et découpes

Plutôt qu'une couleur, une forme peut être remplie avec un dégradé ou un motif.

Les dégradés

Il existe deux types de dégradés : dégradé linéaire et dégradé radial.
La syntaxe des dégradés en SVG est disons ... un peu lourde.

Si vous produisez du SVG via Snap SVG vous pouvez faire l'impasse sur ce chapitre.

Dégradé linéaire

Exemple

Ci-dessous un canevas SVG contenant 4 rectangles. Chaque rectangle rempli avec un dégradé linéaire. balise lineargradient de SVG

Le code SVG correspondant

<svg viewBox ="0 0 700 700" ...> <defs> <lineargradient id="diagonal_bas" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" stop-color="green" /> <stop offset="100%" stop-color="white" /> </lineargradient> <lineargradient id="diagonal_haut" x1="0%" y1="100%" x2="100%" y2="0%"> <stop offset="0%" stop-color="green" /> <stop offset="100%" stop-color="white" /> </lineargradient> <lineargradient id="horizontal"> <stop offset="0%" stop-color="olive" /> <stop offset="50%" stop-color="lime" /> <stop offset="100%" stop-color="white" /> </lineargradient> <lineargradient id="vertical" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="olive" /> <stop offset="50%" stop-color="lime" /> <stop offset="100%" stop-color="white" /> </lineargradient> </defs> <rect fill="url(#diagonal_bas)" x="0" y="0" width="300" height="300"/> <rect fill="url(#diagonal_haut)" x="50%" y="0" width="300" height="300"/> <rect fill="url(#horizontal)" x="0%" y="400" width="300" height="300"/> <rect fill="url(#vertical)" x="50%" y="400" width="300" height="300"/> </svg>

Nous avons vu qu'une forme peut être remplie avec une couleur (opaque ou transparente) ou avec un motif. Nous voyons maintenant qu'une forme peut être remplie avec un dégradé (linéaire ou radial) !

Dans le canevas ci-dessus quatre types de dégradés ayant des axes de dégradé différents ont été définis.
Ensuit chaque dégradé défini est utilisé pour remplir un rectangle.

Syntaxe

On déclare un dégradé linéaire grâce au conteneur linearGradient dans lequel on inclut des balises stop.
La balise linearGradiant doit contenir quatre attributs x1 y1 x2 et y2 qui précisent l'axe du dégradé à moins que nous voulions un dégradé horizontal. Dans ce cas ces quatre attributs sont inutiles !
L'attribut ID est obligatoire pour pouvoir appliquer le dégradé à une forme.
Comme tous les objets réutilisables, linearGradient doit évidemment se trouver dans le conteneur defs.

La balise linearGradiant contient autant de balises stop que de couleurs dans le dégradé.
La balise stop permet de préciser l'une des couleurs du dégradé et comprend trois attributs : offset et stop-color et stop-opacity. Le dernier attribut est facultatif (valeur par défaut =1).

Quant à l'utilisation d'un dégradé linéaire pour remplir une forme c'est exactement comme l'application d'un motif : via l'attribut fill dans la balise correspondant à la forme.

Les dégradés radiaux

Exemple

Ci-dessous deux formes (un cercle et une ellipse) remplies avec le même dégradé radial. SVG - balise radialGradiant

Le code SVG correspondant

<svg width="100%" height="100%" viewBox ="0 0 800 400" style ="background :linear-gradient(to bottom, skyblue ,white) ;"> <defs> <radialGradient id="radial" > <stop offset="0%" stop-color = 'olive' stop-opacity ="1"/> <stop offset="25%" stop-color = 'olive' stop-opacity ="0.8" /> <stop offset="50%" stop-color = 'olive' stop-opacity ="0.6"/> <stop offset="75%" stop-color = 'olive' stop-opacity ="0.4"/> <stop offset="100%" stop-color = 'olive' stop-opacity ="0.2"/> </radialGradient> </defs> <circle cx = '50%' cy = '100' r = '100' fill = 'url(#radial)' /> <ellipse cx = '50%' cy = '300' rx = '150' ry ='100' fill = 'url(#radial)' /> </svg>

La balise SVG est un élément HTML donc rien n'interdit de lui appliquer en guise de "background" un dégradé CSS.
Par contre on ne peut appliquer à un objet SVG qu'un dégradé défini selon la syntaxe SVG.

Un dégradé radial est défini grâce au conteneur radialGradiant avec un attribut obligatoire : ID.
Comme pour un dégradé linéaire il faut ensuite préciser les différentes tons.
Dans l'exemple je joue sur l'opacité de la couleur. J'ai donc un dégradé de la couleur olive.

Application d'un dégradé à un groupe de formes

Peut-on remplir un groupe de formes avec un dégradé ?
Oui mais le dégradé s'applique alors à chaque forme et non pas à l'ensemble !
Il faudrait transformer le groupe de formes en un chemin.

Le code SVG

<svg viewBox ="0 0 600 600" width ="80%" height ="auto"> <defs> <lineargradient id="vertical" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" stop-color="black" /> <stop offset="50%" stop-color="olive" /> <stop offset="100%" stop-color="lime" /> </lineargradient> </defs> <circle cx = '50%' cy = '100' r = '100' fill = 'url(#vertical)' /> <g fill ='url(#vertical)' > <circle cx = '50%' cy = '400' r = '100' /> <rect x="200" y = "400" width ="200" height="200" /> </g> </svg>

Le rendu de ce code

Le dégradé se répète au niveau du cercle et du rectangle constitutifs du groupe.
C'est un peu gênant ...

Les motifs : la balise pattern

Nous savons déjà remplir une forme avec une couleur unie (plus ou moins opaque) et avec un dégradé de couleurs (leçon précédente).
Nous allons voir maintenant que l'on peut remplir une forme avec un motif préalablement défini. Bref produire un fond tel un papier peint.

Un motif est une forme (ou groupe de formes) contenue dans un rectangle et qui se répète dans toute la zone cible.
Un motif peut être aussi une image matricielle importée (ou un groupe d'images).
Avant d'utiliser un motif pour remplir une zone du canevas SVG il faut bien entendu le définir.

Exemple 1 : le motif est un groupe de formes

Le code SVG correspondant (extraits)

<svg viewBox ="0 0 800 200" width="100%" height="auto" > <defs> <pattern id="motif1" x="0" y="0" patternUnits="userSpaceOnUse" width="50" height="50" > <ellipse fill='red' cx="25" cy="20" rx="20" ry="15" /> <circle fill = 'olive' cx="25" cy="20" r = '10' /> </pattern> <pattern id="motif2" x="0" y="0" patternUnits="objectBoundingBox" width="20%" height="25%"> <rect x = '0' y = '0' width = '50' height = '50' fill = 'none' /> <ellipse fill='red' cx="25" cy="20" rx="20" ry="15" /> <circle fill = 'olive' cx="25" cy="20" r = '10' /> </pattern> </defs> <rect x="0" y="0" width="400" height="200" fill = "url(#motif1)" stroke-width ='1px' stroke ='black' /> <rect x="400" y="0" width="400" height="200" fill = "url(#motif2)" stroke-width ='1px' stroke ='black' /> </svg>

Dans cette page nous définissons deux motifs (donc dans le conteneur defs) avec le conteneur pattern et identifiés respectivement motif1 et motif2.

L'attribut ID est obligatoire. Vous allez comprendre pourquoi bientôt.
Ces deux motifs sont pratiquement identiques : un carré de 50 par 50 contenant une ellipse. Cette ellipse contenant elle-même un cercle de couleur "olive".

La seule différence entre les deux motifs est la valeur des attributs patternUnits, width, height.
Ces trois attributs précisent le mode de répétition du motif dans la forme cible.

Pour le premier motif nous avons pour ces trois attributs respectivement les valeurs userSpaceOnUse, 50 et 50 (50 pixels bien sûr ...). Ce qui veut dire que le motif sera répété tous les 50 pixels dans la zone cible.
Pour le deuxième motif et pour les mêmes attributs nous avons les valeurs : objectBoundingBox, 20% et 25%. Ce qui veut dire que le motif sera dupliqué tous les 20% en largeur et 25% en hauteur. Donc 5 fois en largeur et 4 fois en hauteur.

Attention dès que l'attribut patterUnits a pour valeur objectBoundindBox les valeurs des attributs width et height de la balise pattern doivent être exprimées en pourcentage.

Il faut maintenant utiliser ces deux motifs pour remplir des rectangles.
Le canevas SVG est en effet divisé en deux rectangles.
Le premier rectangle (partie gauche de la zone de dessin) est rempli avec motif1 : < rect fill = "url(#motif1)" ...
Le deuxième rectangle (partie droite du canevas) est rempli avec motif2 : < rect fill = "url(#motif2)"...
Donc je pense que vous avez compris pourquoi il faut non seulement définir un motif mais aussi l'identifier.

Motif basé sur une image existante

Un motif peut être une image matricielle ou un groupe d'images !

Exemple

Le code SVG correspondant

... <defs> <pattern id="plongeur1" patternUnits="userSpaceOnUse" x="0" y="0" width="100" height="100" > <image x="0" y="0" width="100" height="100" xlink:href="../images/plongeur3.gif" /> </pattern> <pattern id="plongeur2" patternUnits="objectBoundingBox" x="0" y="0" width="25%" height="100%" > <image x="0" y="0" width="100" height="100" xlink:href="../images/plongeur3.gif" /> </pattern> </defs> <rect x="0" y="0" width="800" height="100" fill = "url(#plongeur1)" /> <rect x="0" y="100" width="800" height="100" fill = "url(#plongeur2)" /> </svg>

On définit un motif identifié "plongeur1" qui a une taille de 100 par 100. Et à l'intérieur du conteneur pattern on utilise la balise isolée image pour importer une image matricielle.
On définit un deuxième motif identifié "plongeur2" qui correspond aussi à une image existante et qui a une taille également de 100 par 100

Les deux motifs se distinguent par le mode de répétition du motif dans la zone cible. Voir page précédente !
Premier motif répété tous les 100px en largeur et tous les 100px en hauteur : ...patternUnits="userSpaceOnUse" x="0" y="0" width="100" height="100"
Deuxième motif répété 4 fois en largeur et 1 fois en hauteur : ...patternUnits="objectBoundingBox" x="0" y="0" width="25%" height="100%"

Il faut maintenant utiliser ces motifs pour remplir deux rectangles.

Un rectangle qui occupe la moitié haute de la zone de dessin a pour fond le motif identifié "plongeur1" :
<rect... fill = "url(#plongeur1)" ... .
Un rectangle qui occupe la moitié basse de la zone de dessin a pour fond le motif identifié "plongeur2" :
<rect ... fill = "url(#plongeur2)" ...

Motif défini à partir de plusieurs images

Le code SVG correspondant

... <defs> <pattern id="couple" patternUnits="userSpaceOnUse" x="0" y="0" width="200" height="100" > <image x="0" y="0" width="100" height="100" xlink:href="../images/plongeur2.gif" /> <image x="100" y="0" width="100" height="100" xlink:href="../images/plongeur3.gif" /> </pattern> </defs> <rect x="0" y="0" width="100%" height="100%" fill = "url(#couple)" /> </svg>

Rappel : on peut exprimer les dimensions d'une forme en pourcentages des dimensions du canevas. C'est le cas ici du rectangle qui sera rempli avec le motif.

On définit un motif identifié couple composé de deux images existantes placées côte à côte (elles pourraient être l'une en dessous de l'autre).
Comme chaque image fait 100 par 100 le motif fait donc 200 par 100.
On demande que le motif soit répété tous les 200px en largeur et tous les 100px en hauteur : patternUnits="userSpaceOnUse" x="0" y="0" width="200" height="100"

Masques et découpes

Nous allons aborder deux nouvelles balises du format SVG : mask et clipath.

L'outil masque

On peut souhaiter appliquer au dessin le même effet que si il était vu au travers d'un rideau translucide. Il faut alors utiliser la fonction masque.

Exemple

Le haut de l'image est masqué. Le bas de l'image est identique à l'original.

Le code SVG correspondant

... <defs> <!-- définition du masque --> <mask id="masque"> <rect width="500" height="100" x ="0" y ="0" fill="white" fill-opacity ="0"/> <rect width="500" height="100" x ="0" y ="100" fill="white" fill-opacity =".2"/> <rect width="500" height="100" x ="0" y ="200" fill="white" fill-opacity =".4"/> <rect width="500" height="100" x ="0" y ="300" fill="white" fill-opacity =".6"/> <rect width="500" height="100" x ="0" y ="400" fill="white" fill-opacity =".8"/> <rect width="500" height="100" x ="0" y ="500" fill="white" fill-opacity ="1"/> </mask> </defs> <image xlink:href="../images/jolie_fille.jpg" width="500" height="600" x="0" y ="0" mask = 'url(#masque)'/> ...

Ici on définit avec la balise mask un masque composite : 6 rectangles. Chaque rectangle a une largeur de 500 et une hauteur de 100 et est rempli de blanc avec une opacité croissante (augmentation de 20% à chaque fois).
Ce masque s'applique à une image de 500 par 600 grâce via l'attribut mask.

La découpe

On peut souhaiter l'effet d'un objet vu à travers un trou de serrure ou une paire de jumelles. Il faut alors utiliser la fonction découpe.

Exemple

Utilisation d’une découpe grâce à la balise clipPath

Le code SVG correspondant

<svg width="50%" height="auto" viewBox="0 0 400 300" style ="box-shadow : 5px 5px 5px gray;"> <title>Utilisation d’une découpe grâce à la balise clipPath</title> <defs> <!-- définition de la découpe --> <clipPath id="decoupe"> <ellipse cx="50%" cy="50%" rx="200" ry = "150" /> </clipPath> </defs> <image xlink:href="../images/chalets.jpg" width="400" height="300" clip-path = 'url(#decoupe)'/> </svg>

Grâce au conteneur clipPath on définit ici une découpe simple : une ellipse.
Puis on affiche une image avec cette découpe en utilisant l'attribut clip-path à la balise image

Découpe : autre exemple

Jolie fille vue au travers d'un trou de serrure.
La découpe peut être un groupe de formes.

Ici il s'agit de l'incorporation dans la page d'un fichier SVG (decoupe.svg)

Le code pour l'insertion de l'image SVG dans la page :

<object type="image/svg+xml" data="decoupe.svg" width ="50%"></object>

Il faut incorporer l'image vectorielle avec la balise object car le code SVG dans ce fichier contient des liens vers d'autres images.

Le fichier "decoupe.svg"

Quant au code du fichier SVG, il est le suivant :

... <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox ="0 0 500 600" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <clipPath id="trou_serrure"> <circle cx="50%" cy="200" r="150" /> <polygon points ="250,200 150,500 350,500" /> </clipPath> </defs> <image xlink:href="../images/jolie_fille.jpg" width="500" height="600" clip-path = "url(#trou_serrure)" /> </svg>

Deux formes (un cercle et un polygone sont contenu dans la balise clippath.