1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
|
---
title: Motifs
slug: Web/SVG/Tutorial/Patterns
tags:
- SVG
- SVG:Tutoriel
translation_of: Web/SVG/Tutorial/Patterns
original_slug: Web/SVG/Tutoriel/Motifs
---
<p>{{ PreviousNext("Web/SVG/Tutoriel/Gradients", "Web/SVG/Tutoriel/Texts") }}</p>
<p>Les motifs (<em>patterns</em> en anglais) sont sans aucun doute les types de remplissages les plus complexes à utiliser en SVG. Ce sont également des outils très puissants, ils méritent donc d'être abordés pour que vous en connaissiez les fondamentaux. Comme les dégradés, l'élément {{SVGElement('pattern')}} doit être placé dans la section <code><defs></code> du fichier SVG.</p>
<h2 id="Exemple">Exemple</h2>
<pre class="brush: html"><svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white"/>
<stop offset="95%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red"/>
<stop offset="95%" stop-color="orange"/>
</linearGradient>
<pattern id="Pattern" x="0" y="0" width=".25" height=".25">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</defs>
<rect fill="url(#Pattern)" stroke="black" width="200" height="200"/>
</svg></pre>
<p>{{ EmbedLiveSample('Exemple','220','220','/files/725/SVG_Pattern_Example.png') }}</p>
<p>À l'intérieur de l'élément <code>pattern</code>, vous pouvez inclure toutes les formes de bases de SVG et les styliser de la même manière que d'habitude (remplissage, contour, dégradés, opacité, etc). Dans notre exemple, on a dessiné un cercle et deux rectangles (qui se chevauchent et dont l'un est deux fois plus grand que l'autre pour remplir le motif en entier).</p>
<p>La partie pouvant apporter le plus de confusion avec les motifs est le système d'unité et la taille des éléments.</p>
<h2 id="Unités_du_motif_objectBoundingBox">Unités du motif: objectBoundingBox</h2>
<p>Les attributs <code>width</code> et <code>height</code> sur l'élément <code>pattern</code> décrivent jusqu'où le motif doit aller avant de se répéter. Les attributs <code>x</code> et <code>y</code> sont également disponibles si vous souhaitez décaler le point de départ du motif à l'intérieur du dessin.</p>
<p>Même principe que l'attribut <code>gradientUnits</code> (que nous avons vu précédemment avec les dégradés), les motifs peuvent prendre un attribut <code>patternUnits</code>, pour spécifier l'unité utilisée par le motif. La valeur par défaut est "objectBoundingBox", ainsi une taille de 1 remplira entièrement la hauteur/largeur de l'objet auquel le motif est appliqué. Puisque dans notre cas, on veut que le motif se répète 4 fois horizontalement et verticalement, on a définit <code>height</code> et <code>width</code> à 0.25. Cela signifie que la hauteur et largeur du pattern sera de 25% celle de l'objet.</p>
<p>De même, pour que le motif commence à 10 pixels du bord supérieur-gauche de l'objet, il faudrait définir les valeurs de <code>x</code> et <code>y</code> à 0.05 (10/200 = 0.05).</p>
<h2 id="Unités_du_contenu_userSpaceOnUse">Unités du contenu: userSpaceOnUse</h2>
<p>Contrairement aux dégradés, les motifs ont un deuxième argument, <code>patternContentUnits</code>, qui lui spécifie l'unité utilisée par les formes à l'intérieur du motif. La valeur par défaut est "userSpaceOnUse", l'opposé de l'attribut <code>patternUnits</code>. Cela signifie qu'à moins de définir ces attributs aurement (<code>patternContentUnits</code> et/ou <code>patternUnits</code>), les formes que vous dessinez à l'intérieur du motif ont un système de coordonnées différent du motif, ce qui peut rendre les choses un peu déroutantes si vous écrivez le code à la main.</p>
<p>Pour que cela fonctionne dans l'exemple ci-dessus, nous avons dû prendre en compte la taille du rectangle sur lequel est appliqué le motif (200px) et le fait que l'on veut répéter le motif 4 fois horizontalement et verticalement, donc que le motif sera un carré de 50x50. Les deux rectangles et le cercle à l'intérieur du motif ont été dimensionnés pour tenir dans un carré de 50x50. Tout ce qui sortirait en dehors ne serait pas affiché.</p>
<p>La chose à retenir est que si l'objet change de taille, le motif lui-même sera mis à l'échelle mais les objets à l'intérieur non. Ainsi, alors qu'on aura toujours 4 motifs qui se répètent horizontalement et verticalement, les objets à l'intérieur du motif garderont la même taille, et une zone vide sera affichée.</p>
<h3>Exemple</h6>
<pre class="brush: html hidden"><svg width="600" height="200" xmlns="http://www.w3.org/2000/svg" id="svg" class="playable-svg">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white"/>
<stop offset="95%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red"/>
<stop offset="95%" stop-color="orange"/>
</linearGradient>
<pattern id="Pattern" x="0" y="0" width=".25" height=".25">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</defs>
<rect fill="url(#Pattern)" stroke="black" width="200" height="200"/>
</svg>
<div class="playable-buttons">
<input id="edit" type="button" value="Edit" />
<input id="reset" type="button" value="Reset" />
</div>
<textarea id="code" class="playable-code">
rect.setAttribute('width', 300);</textarea>
</pre>
<pre class="brush: js hidden">var svg = document.getElementById('svg'),
rect = svg.lastElementChild;
var textarea = document.getElementById('code'),
reset = document.getElementById('reset'),
edit = document.getElementById('edit'),
code = textarea.value;
function drawSvg() {
eval(textarea.value);
}
reset.addEventListener('click', function() {
textarea.value = code;
drawSvg();
});
edit.addEventListener('click', function() {
textarea.focus();
})
textarea.addEventListener('input', drawSvg);
window.addEventListener('load', drawSvg);
</pre>
<p>{{ EmbedLiveSample('exemple_jouable','220','350') }}</p>
<h2 id="Unités_du_contenu_objectBoundingBox">Unités du contenu: objectBoundingBox</h2>
<p>En changeant l'attribut <code>patternContentUnits</code>, on peut utiliser le même système d'unité pour tous les éléments:</p>
<pre class="brush: xml"> <pattern id="Pattern" width=".25" height=".25" patternContentUnits="objectBoundingBox">
<rect x="0" y="0" width=".25" height=".25" fill="skyblue"/>
<rect x="0" y="0" width=".125" height=".125" fill="url(#Gradient2)"/>
<circle cx=".125" cy=".125" r=".1" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</pre>
<p>Maintenant, parce le contenu du motif utilise le même système d'unité que le motif, le motif redimensionne automatiquement son contenu. Cela contraste avec le système "userSpaceOnUse" par défaut, où lorsque le motif change le taille, le contenu garde la même taille.</p>
<h3 id="code_jouable_2">Code jouable 2</h3>
<pre class="brush: html hidden"><svg width="600" height="200" xmlns="http://www.w3.org/2000/svg" id="svg" class="playable-svg">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white"/>
<stop offset="95%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red"/>
<stop offset="95%" stop-color="orange"/>
</linearGradient>
<pattern id="Pattern" width=".25" height=".25" patternContentUnits="objectBoundingBox">
<rect x="0" y="0" width=".25" height=".25" fill="skyblue"/>
<rect x="0" y="0" width=".125" height=".125" fill="url(#Gradient2)"/>
<circle cx=".125" cy=".125" r=".1" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</defs>
<rect fill="url(#Pattern)" stroke="black" width="200" height="200"/>
</svg>
<div class="playable-buttons">
<input id="edit" type="button" value="Edit" />
<input id="reset" type="button" value="Reset" />
</div>
<textarea id="code" class="playable-code">
rect.setAttribute('width', 300);</textarea>
</pre>
<pre class="brush: js hidden">var svg = document.getElementById('svg'),
rect = svg.lastElementChild;
var textarea = document.getElementById('code'),
reset = document.getElementById('reset'),
edit = document.getElementById('edit'),
code = textarea.value;
function drawSvg() {
eval(textarea.value);
}
reset.addEventListener('click', function() {
textarea.value = code;
drawSvg();
});
edit.addEventListener('click', function() {
textarea.focus();
})
textarea.addEventListener('input', drawSvg);
window.addEventListener('load', drawSvg);
</pre>
<p>{{ EmbedLiveSample('code_jouable_2','220','350') }}</p>
<div class="note">
<p><strong>Note :</strong> Dans Gecko, les cercles semblent avoir du mal à être dessinés si le rayon est inférieur à 0.075 (on ignore s'il s'agit d'un bug de l'élément pattern ou non). Pour contourner ce problème, il est probablement préférable d'éviter de dessiner des cercles dans des unités "objectBoundingBox".</p>
</div>
<h2 id="Unités_du_motif_userSpaceOnUse">Unités du motif: userSpaceOnUse</h2>
<p>Aucune des utilisations vu jusqu'ici ne correspond à l'usage habituel des motifs (tel qu'on le ferait en CSS): les motifs ont généralement une taille définie et se répètent indépendamment de la taille de l'objet sur lequel il est appliqué. Pour créer quelque chose comme ça, le motif et le contenu doivent être dessiné en mode "userSpaceOnUse":</p>
<pre class="brush: xml"> <pattern id="Pattern" x="10" y="10" width="50" height="50" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</pre>
<p>Bien sûr, cela veut dire que le motif ne sera pas mis à l'échelle si vous modifiez la taille de l'objet ultérieurement.</p>
<h3>Exemple jouable</h3>
<pre class="brush: html hidden"><svg width="600" height="200" xmlns="http://www.w3.org/2000/svg" id="svg" class="playable-svg">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white"/>
<stop offset="95%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red"/>
<stop offset="95%" stop-color="orange"/>
</linearGradient>
<pattern id="Pattern" x="10" y="10" width="50" height="50" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</defs>
<rect fill="url(#Pattern)" stroke="black" width="200" height="200"/>
</svg>
<div class="playable-buttons">
<input id="edit" type="button" value="Edit" />
<input id="reset" type="button" value="Reset" />
</div>
<textarea id="code" class="playable-code">
rect.setAttribute('width', 300);</textarea>
</pre>
<pre class="brush: js hidden">var svg = document.getElementById('svg'),
rect = svg.lastElementChild;
var textarea = document.getElementById('code'),
reset = document.getElementById('reset'),
edit = document.getElementById('edit'),
code = textarea.value;
function drawSvg() {
eval(textarea.value);
}
reset.addEventListener('click', function() {
textarea.value = code;
drawSvg();
});
edit.addEventListener('click', function() {
textarea.focus();
})
textarea.addEventListener('input', drawSvg);
window.addEventListener('load', drawSvg);
</pre>
<p>{{ EmbedLiveSample('exemple_jouable_3','220','350') }}</p>
<h2 id="Récapitulatif">Récapitulatif</h2>
<p>Les trois exemples sont illustrés ci-dessous sur un rectangle allongé à une hauteur de 300px:</p>
<p><img src="svg_pattern_comparison_of_units.png"></p>
<p>{{ PreviousNext("Web/SVG/Tutoriel/Gradients", "Web/SVG/Tutoriel/Texts") }}</p>
|