Blog
Aller au contenu

Guillaume Duverger

Développement & graphisme


Accueil » Développement » Apprendre SVG » Masques et découpages SVG

Sommaire
  1. Masque et découpage en SVG c'est quoi ?
  2. Le découpage avec l'élément SVG clipPath
    1. Méthode SVG inline
    2. Méthode SVG externe
    3. Exemples avancés
    4. Exemple animations/transitions avec CSS
    5. Exemple animation avec SMIL
    6. Exemple animation avec JavaScript
  3. Le masquage avec l'élément SVG mask
    1. Exemple animations/transitions avec CSS
    2. Exemple animation avec SMIL
    3. Exemple animation avec JavaScript
  4. Aller plus loin
Télécharger ce cours en pdf

👉 Masque et découpage en SVG c'est quoi ?

Tout comme avec CSS, l'écrêtage (ou découpage) et le masquage sont réalisables avec SVG grâce aux éléments clipPath et mask.

Le découpage consiste à cacher tout ou partie d'un élément graphique que ce soit avec une forme de base (élément SVG circle par exemple), un tracé (path) ou encore du texte (text). Autrement dit, tout ce qui se trouve à l'intérieur du tracé de détourage est visible, tandis que l'extérieur est caché.

Le masquage est, quant à lui, constitué d'une forme, d'un chemin, d'une image, d'un dégradé, ou encore d'un motif. Son rôle va être de couvrir tout ou partie d'un élément en contrôlant le degré de transparence de ce dernier.

Pour celles et ceux qui n'auraient pas perçu la différence entre découpage et masquage SVG, un dessin valant mieux qu'un long discours :

Différence découpage et masquage SVG

👉 Le découpage avec l'élément clipPath

Il y a deux façons d'utiliser le découpage en SVG : avec du SVG directement dans le document HTML (SVG inline) ou avec un fichier SVG externe (SVG external).

Dans les deux cas, il faudra également une référence CSS via la propriété clip-path ou SVG avec la même propriété.

Une référence CSS se compose comme ceci :



/*cette classe est à mettre dans l'élément qe l'on souhaite détourer */

.class {
 
  clip-path: url("#svg-inline");/*cet identifiant doit être le même que dans l'élément SVG clipPath*/
}

Une référence SVG se compose comme ceci :



<svg height=0 width=0>
<defs>
<clipPath id="exemple">
<circle x=0 y=0 r=50/>
</clipPath>
</defs>
</svg>
<img style="clip-path:url(#exemple)" src="image.jpg" alt>

/* ou comme ceci */

<svg height="" width="" viewBox="0 0 ... ...">
<defs>
<clipPath id="exemple">
<circle x=0 y=0 r=50/>
</clipPath>
</defs>
<image width="100%" height="100%" clip-path=url(#exemple) href="image.jpg"/>
</svg>



Avant de voir les différentes méthodes pour appliquer un détourage en SVG, voyons les pricipaux attributs (en effet, ils sont nombreux🔗) de l'élément clipPath.

L'attribut clipPathUnits va se révéler très utile, puisqu'il va définir le système de coordonnées du détourage. Il peut prendre deux valeurs :

  • userSpaceOnUse (par défaut) : représente les valeurs du système de coordonnées utilisateur en place au moment où le détourage est référencé.
  • objectBoundingBox : représente les fractions ou les pourcentages de la zone de délimitation de l'élément auquel le détourage est appliqué.

💡 Un autre attribut important dans le détourage en SVG est clip-rule. Associé à l'élément clipPath, il définit l'algorithme à utiliser pour remplir les différentes parties d'un graphique. Il en va de même pour l'attribut fill-rule.


La méthode SVG inline :

Un exemple ci-dessous avec le code associé :

exemple clipPath SVG inline


<img src="image.jpg" class="svg-inline" alt>

<svg height=0 width=0>
<defs>
<clipPath id="svg-inline" clipPathUnits="objectBoundingBox" >
<polygon points="0 0, 0 0, 1 0, 1 1" />
</clipPath>
</defs>
</svg> 




.svg-inline {
 
  clip-path: url("#svg-inline");
}

Comme vous pouvez le constater, c'est assez simple à mettre en place. On indique une image dans le code HTML, puis en dessous le code SVG qui va nous servir à découper cette image. Dans l'exemple, la forme choisie est un polygone. Nous avons mis un identifiant au sein de l'élément SVG clipPath, que nous avons ensuite reporté dans une classe en CSS en utilisant la propriété clip-path.

Autre exemple SVG inline avec des formes simples :

exemple SVG inline avec des formes simples


<img src="image.jpg" alt class="polygone-etoile">
<img src="image.jpg" alt class="polygone-isocele">
<img src="image.jpg" alt class="polygone-hexagone">
<img src="image.jpg" alt class="polygone-equilateral">
<img src="image.jpg" alt class="polygone-losange">  
<img src="image.jpg" alt class="polygone-rectangle">
<img src="image.jpg" alt class="polygone-carre">
<svg height=0 width=0>
<defs>
<clipPath id="polygone-carre" clipPathUnits="objectBoundingBox">
<polygon points="0.1 0.1, 0.9 0.1, 0.9 0.9, 0.1 0.9" />
</clipPath>
<clipPath id="polygone-rectangle" clipPathUnits="objectBoundingBox">
<polygon points="0.1 1, 0.1 0, 0.9 0, 0.9 1" />
</clipPath>	
<clipPath id="polygone-losange" clipPathUnits="objectBoundingBox">
<polygon points="0.5 0, 1 0.5, 0.5 1, 0 0.5" />
</clipPath>
<clipPath id="polygone-equilateral" clipPathUnits="objectBoundingBox">
<polygon points="0 0.87, 0.5 0, 0.5 0, 1 0.87" />
</clipPath>
<clipPath id="polygone-isocele" clipPathUnits="objectBoundingBox">
<polygon points="0 1, 0.5 0, 0.5 0, 1 1" />
</clipPath>	
<clipPath id="polygone-etoile" clipPathUnits="objectBoundingBox">
<polygon points="0.5 0, 0.63 0.38, 1 0.38, 0.69 0.59, 0.82 1, 0.5 0.75, 0.18 1, 0.31 0.59, 0 0.38, 0.37 0.38" />
</clipPath>	
<clipPath id="polygone-hexagone" clipPathUnits="objectBoundingBox">
<polygon points="0.5 0, 1 0.25, 1 0.75, 0.5 1, 0 0.75, 0 0.25" />
</clipPath>
</defs>
</svg>




.polygone-carre{
	
clip-path: polygon(10% 10%,90% 10%,90% 90%,10% 90%);
clip-path: url("#polygone-carre")

	}
	
.polygone-losange{

clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
clip-path: url("#polygone-losange")

}

.polygone-rectangle{

clip-path: polygon(10% 100%, 10% 0%, 90% 0%, 90% 100%);
clip-path: url("#polygone-rectangle")

}

.polygone-equilateral{

clip-path: polygon(0% 87%, 50% 0%, 50% 0%, 100% 87%);
clip-path: url("#polygone-equilateral")

}
.polygone-isocele{

clip-path: polygon(0% 100%, 50% 0%, 50% 0%, 100% 100%);
clip-path: url("#polygone-isocele")

}

.polygone-hexagone{

clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
clip-path: url("#polygone-hexagone")

}

.polygone-etoile{ 

clip-path: polygon(50% 0%, 63% 38%, 100% 38%, 69% 59%, 82% 100%, 50% 75%, 18% 100%, 31% 59%, 0% 38%, 37% 38%);
clip-path: url("#polygone-etoile")

}


Exemple sur du texte avec une autre méthode SVG inline :

exemple SVG inline avec texte

Le code ci-dessous :



<svg height="" width="" viewBox="0 0 ... ...">
<defs>
<clipPath id="clip-path-text">
<text x="50%" y="160" style="text-anchor:middle;font-size:10rem;font-family:'Alfa Slab One'">SVG</text>
</clipPath>
</defs>
<image width="100%" height="100%" clip-path=url(#clip-path-text) href="image.jpg"/>
</svg>


Cette fois-ci, nous avons utilisé l'élément SVG image directement au sein du SVG et avons placé la propriété CSS clip-path à l'intérieur de l'élément.


La méthode SVG externe :

Reprenons le premier exemple avec le polygone :

exemple clipPath SVG externe


<img class="svg-externe" src="image.jpg" alt>


/*le fichier externe clip.svg*/

<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="svg-ext" clipPathUnits="objectBoundingBox">
<polygon points="0 0, 0 0, 1 0, 1 1" />
</clipPath>
</defs>	
</svg>




.svg-externe {

  clip-path: url("clip.svg#svg-ext");
}    
	


💡 Pour que le découpage fonctionne avec la méthode SVG externe, vous devez impérativement déclarer l'attribut xmlns dans la balise <svg>. En revanche, il ne semble pas y avoir de support pour le moteur Blink (Chrome, Safari...).


Exemples avancés :

Nous avons vu des exemples simples de découpage en SVG. Corsons un peu les choses en utilisant plusieurs formes dans un même découpage.

Exemple avancé découpage SVG

Le code ci-dessous :



<img class="multi-formes" src="image.jpg" alt>

<svg height=0 width=0>
<defs>
<clipPath id="multi-formes">
<circle cx="180" cy="120" r="80"/>
<circle cx="350" cy="60" r="50"/>
<circle cx="540" cy="200" r="50"/>
<circle cx="310" cy="270" r="80"/>
<path d="m531.43 148.57-34.692-18.848-35.193 17.893 7.2048-38.818l-27.89-27.932 39.14-5.144 17.95-35.162 16.988 35.639 38.989 6.2103-28.645 27.169z"/>
<path d="m185.71 165.71-48.3-12.13-37.37 32.92-3.395-49.69-42.853-25.37 46.208-18.583 10.89-48.597l31.948 38.205 49.584-4.6589-26.463 42.19z"/>
<path d="m400 211.43-55.838-31.685-57.757 28.035 12.879-62.896-44.511-46.267 63.798-7.187 30.248-56.63 26.55 58.455 63.205 11.268-47.389 43.314z"/>
<path d="m231.43 362.86-41.387-36.701-53.697 13.287 22.116-50.703-29.23-46.963 55.055 5.3652 35.631-42.312 11.91 54.019 51.252 20.813-47.694 28.02z"/>
<path d="m462.86 334.29-32.99-19.687-34.921 16.016 8.5291-37.459-26.023-28.262 38.262-3.4638 18.838-33.483 15.118 35.318 37.665 7.5689-28.918 25.292z"/>
</clipPath>
</defs>
</svg>




.multi-formes{
	
	clip-path:url(#multi-formes)
	
	
	}
	

Vous l'aurez compris, il y a énormément de possibilités.


Exemples animations/transitions avec CSS :

Exemple d'animation :

SVG


<img class=demo_anim_svg1 src="image.jpg" alt>

<svg height="0" width="0">
<defs>
<clipPath id="clip-path-text">
<text x="50%" y="160" style="text-anchor:middle;font-size:10rem;font-family:'Alfa Slab One'">SVG</text>
</clipPath>
</defs>
</svg>




.demo_anim_svg1{

clip-path:url(#clip-path-text);
animation: demo_anim_svg1 5s infinite alternate
	
	}

	
@keyframes demo_anim_svg1{
	
	from{
		object-position: 0 0 
	} 
	
	to{
		object-position: -80px 0
	}
	
	}



Exemple de transition :



<div class="demo_anim_svg"></div>

<svg height=0 width=0>
<defs>
<clipPath id="cercle_anim" clipPathUnits="objectBoundingBox">
<circle cy='.5' cx='.5' r='.5'/>
</clipPath>
</defs>
</svg>  

<svg height=0 width=0>
<defs>
<clipPath id="cercle_anim1" clipPathUnits="objectBoundingBox">
<circle cy='.5' cx='.5' r='.25'/>
</clipPath>
</defs>
</svg>




.demo_anim_svg {
	
width:300px;
height:300px;
position:relative;
/*clip-path:circle(50% at 50% 50%);*/
clip-path:url("#cercle_anim");
margin:2rem auto;
background:url("image.jpg") no-repeat center/cover
	
}

.demo_anim_svg:before{
	
content:'';
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
clip-path:url("#cercle_anim1");
background:url("image.jpg") no-repeat center/cover;
filter:blur(10px);
width:0;
height:0;
transition:.4s ease-in;
pointer-events:none
	
	}

.demo_anim_svg:hover:before{

filter:initial;
height:100%;
width:100%
	
 }



Autre exemple de transition :

Exemple transition CSS


<img src="image.jpg" class="zoom_img" alt>
<svg height=0 width=0>
<defs>
<clipPath id="demo_img" clipPathUnits="objectBoundingBox">
<polygon points="0.5 0, 0.5 0, 1 0.5, 0.5 1, 0.5 1, 0 0.5"/>
</clipPath>
</defs>
</svg>
<svg height=0 width=0>
<defs>
<clipPath id="demo_img_hover" clipPathUnits="objectBoundingBox">
<polygon points="0 0, 1 0, 1 0.5, 1 1, 0 1, 0 0.5"/>
</clipPath>
</defs>
</svg>




.zoom_img{

clip-path: url(#demo_img);
transition: .3s .3s transform,.3s box-shadow;
will-change: transform;
object-fit: cover
	
	}

.zoom_img:hover{

clip-path: url(#demo_img_hover);
transform: scale3d(1.4,1.4,1.4);
box-shadow: 0 0 .625rem hsla(0,0%,0%,.3)
	
	}
	


Animation avec SMIL :

Pour celles et ceux qui ne connaissent pas le langage SMIL, un petit coup d'oeil sur ce blog dans la rubrique SVG sera salutaire.

Ci-dessous, un simple exemple où il s'agit de manipuler les points d'une forme SVG avec le langage d'animation SMIL, propre à SVG.



<svg height="" width="" viewBox="0 0 ... ...">
<defs>
<clipPath id="decoupe-smil" clipPathUnits="objectBoundingBox">
<polygon>
<animate attributeName="points" dur="3s" repeatCount="indefinite" 
values=".5 0, .5 0, 1 .5, .5 1, .5 1, 0 .5;
		
        0 0, 1 0, 1 .5, 1 1, 0 1, 0 .5;
		
        0 0, 1 0, 1 .5, 1 1, 0 1, 0 .5;
		
        .5 0, .5 0, 1 .5, .5 1, .5 1, 0 .5"/>
	
</polygon>
</clipPath>
</defs>
<image clip-path="url(#decoupe-smil)" height="100%" width="100%" href="image.jpg"/>
</svg>


Autre exemple, en jouant cette fois-ci sur le rayon d'un cercle :



<svg height="" width="" viewBox="0 0 ... ...">
<defs>	
<clipPath id="decoupe-smil1">	
<circle cx="300" cy="200" r="50">
<animate attributeName="r" from="0" to="600" begin="0s" dur="3s" fill="freeze" repeatCount="indefinite"/>
</circle>                 
</clipPath>
</defs>
<image clip-path="url(#decoupe-smil1)" height="100%" width="100%" href="image.jpg"/>
</svg>



Animation avec JavaScript :

Il est également possible d'animer du SVG (et plus particulièrement l'élément clipPath puisque c'est le sujet de cet article) avec JavaScript. Il existe de nombreuses bibliothèques.

Dans l'exemple ci-dessous, nous utiliserons GSAP :

Un exemple avancé🔗.

👉 Le masquage avec l'élément SVG mask

Le principe du masquage est assez similaire au découpage. On peut observer une réelle différence lorsqu'on utilise les dégradés. En effet, en créant un dégradé sur une forme, une image, un tracé ou du texte, nous allons utiliser l'opacité.

Il nous faut également parler des attributs de l'élément mask qui sont les suivants :

  • x
  • y
  • width
  • height
  • maskUnits
  • maskContentUnits

Les attributs maskUnits et maskContentUnits prennent tous deux les mêmes valeurs, à savoir :

  • userSpaceOnUse : représente les valeurs du système de coordonnées utilisateur en place au moment où le masque est référencé
  • objectBoundingBox : représente les fractions ou les pourcentages de la zone de délimitation de l'élément auquel le masque est appliqué.

Tandis que l'attribut maskUnits fait directement référence au masque, l'attribut maskContentUnits, quant à lui, fait référence au contenu enfant du masque.

💡 Les couleurs (RGB ; rouge, vert et bleu) et l'opacité (ou canal alpha) font donc partie intégrante du masquage en SVG. Ainsi, la couleur de la forme du masquage va t-elle définir l'opacité de la forme qui utilise le masque. Plus la couleur est proche du blanc, plus la forme du masque sera opaque (pas de transparence). Au contraire, plus celle-ci s'approche du noir, plus elle sera transparente.

Un exemple ci-dessous :

Exemple masque SVG

Un autre exemple sur une image (on applique un dégradé linéaire) :



<svg>
<mask id="mask-degrade" maskContentUnits="objectBoundingBox">
<rect width="1" height="1" fill="url(#degrade)"/>
<linearGradient x2="0" y2="1" id="degrade">
<stop offset="0%" stop-color="white"/>
<stop offset="80%" stop-color="black"/>
</linearGradient>
</mask>
</svg>


<img src="image.jpg" class="masque-degrade" alt>



 .masque-degrade{
 
 mask: url(#mask-degrade); 
 
 
 } 

On peut également mettre une image animée (gif, apng, video...) :

Exemple avec du texte :

Exemple masque texte SVG


<svg height="" width="" viewBox="0 0 ... ...">
<defs>
<mask id="image-mask">
<text text-anchor=middle font-family='Alfa Slab One' font-size=70 x=50% y=120 fill=white>SVG text mask</text>
</mask>
</defs>
<image width="100%" height="100%" preserveAspectRatio="xMidYMid slice" href="image.jpg" mask="url(#image-mask)"/>
</svg>


Ajoutons maintenant à ce texte un pattern (motif) :

Exemple masque texte SVG


<svg height="" width="" viewBox="0 0 ... ...">
<defs>
<pattern id="pattern" width="1" height="3" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect x="0" y="0" width="1" height="1.2" fill="#01FF89"/>
</pattern>
<text x=50% y=120 id="text" text-anchor=middle font-family='Alfa Slab One' font-size="60">SVG text mask</text>
<mask id="texte-pattern">
<rect width="100%" height="100%" fill="white"/>
</mask>
</defs>
<use x="6" y="6" href="#text" fill="url(#pattern)" mask="url(#texte-pattern)"/>
<use x="0" y="0" href="#text" fill="#7A5FFF"/> 
</svg>


Allez soyons fou, ajoutons une vidéo à ce texte.

Méthode SVG inline :

MASK VIDEO


<video autoplay loop preload src="video.mp4"></video>





.cadre-vid-mask {
  position: relative;
  width: 100%;
  margin: 0 auto;
  max-width: 600px
	  
}
	
.cadre-vid-mask video {
  display: flex;
  width: 100%
}
	
.cadre-vid-mask svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%
}
	
.cadre-vid-mask svg text {
	
  font-family: 'Alfa Slab One', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  font-size: 38px
}
	
.cadre-vid-mask svg >rect {
	
-webkit-mask: url(#mask-video);
mask: url(#mask-video)
	
}
	
.cadre-vid-mask svg rect {
	
	fill: #fff
}


Méthode SVG externe :



<video class=video autoplay preload loop src="vid.mp4" type="video/mp4">

</video>




<svg width="600" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 200">
  <defs>
	<style type="text/css">
	<![CDATA[
		#texte {
		 font-family: Arial, Helvetica, sans-serif; font-weight: bold; font-size: 60px; 
		 fill: #fff; stroke: #fff; stroke-width: 0; 
		}
	]]>
	</style>
	<mask id="video-mask" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse">
		<text id="texte" text-anchor="middle" x="50%" y="110">SVG MASK</text>
	</mask>
  </defs>
  <use href="#texte" />
</svg>




.video{ 

display: flex;
margin: 0 auto;
width: 600px;
height: 200px;
mask: url('text-mask.svg#video-mask') no-repeat center center; 
-webkit-mask: url('text-mask.svg#video-mask') no-repeat center center

}



Animation avec CSS :

Exemple d'animation CSS en utilisant les propriétés fill et stroke :



<svg width="420" height="279" viewBox="0 0 420 279">
<style>

#mask-anim polygon{
fill: white;
stroke: white;
animation:anim-masque 5s ease-in-out infinite;
}



@keyframes anim-masque {
50% {
stroke-width: 75;
fill-opacity: 1;
  }
}
</style>
<pattern id="mask-anim" width="100" height="100" viewBox="0 0 120 120" patternUnits="userSpaceOnUse">
<polygon points="7.5 11.25 3.09161061 13.5676275 3.93353806 8.65881373 0.367076128 5.18237254 5.2958053 4.46618627 7.5 0 9.7041947 4.46618627 14.6329239 5.18237254 11.0664619 8.65881373 11.9083894 13.5676275" transform="translate(0,0)"/>
<polygon points="7.5 11.25 3.09161061 13.5676275 3.93353806 8.65881373 0.367076128 5.18237254 5.2958053 4.46618627 7.5 0 9.7041947 4.46618627 14.6329239 5.18237254 11.0664619 8.65881373 11.9083894 13.5676275" transform="translate(120,0)"/>
<polygon points="7.5 11.25 3.09161061 13.5676275 3.93353806 8.65881373 0.367076128 5.18237254 5.2958053 4.46618627 7.5 0 9.7041947 4.46618627 14.6329239 5.18237254 11.0664619 8.65881373 11.9083894 13.5676275" transform="translate(60,60)"/>
<polygon points="7.5 11.25 3.09161061 13.5676275 3.93353806 8.65881373 0.367076128 5.18237254 5.2958053 4.46618627 7.5 0 9.7041947 4.46618627 14.6329239 5.18237254 11.0664619 8.65881373 11.9083894 13.5676275" transform="translate(0,120)"/>
<polygon points="7.5 11.25 3.09161061 13.5676275 3.93353806 8.65881373 0.367076128 5.18237254 5.2958053 4.46618627 7.5 0 9.7041947 4.46618627 14.6329239 5.18237254 11.0664619 8.65881373 11.9083894 13.5676275" transform="translate(120,120)"/>
</pattern>
<mask id="anim-masque">
<rect width="100%" height="100%" fill="url(#mask-anim)"/>
</mask>
<image mask="url(#anim-masque)" href="image.jpg" width="100%" height="100%"/>
</svg>



Animation avec SMIL :


<svg height="300" width="300" viewBox="0 0 300 300">
<defs>
<pattern id="smil-pattern" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="5" cy="5" r="5" fill="white"/>
<animate
attributeName="x"
from="-200"
to="200"
dur="4s"
fill="freeze"
repeatCount="indefinite"/>
</pattern>
<mask id="pattern-mask" maskunits="userSpaceOnUse" maskcontentunits="userSpaceOnUse">
<circle cx="100" cy="100" r="100" fill="url(#smil-pattern)"/>
</mask>
</defs>
<g mask="url(#pattern-mask)">
<circle cx="100" cy="100" r="100"/>
<image preserveAspectRatio="xMidYMid slice" href="images/1-mini.jpg" width="100%" height="100%"/>	
</g>
</svg>



Animation avec JavaScript :

Nous allons reprendre le même exemple d'animation que nous avons utilisé pour l'élément clipPath avec la bibliothèque GSAP :