SVG avancé
au-delà des icônes
Le SVG ne sert pas qu'à afficher des logos. Tracés qui se dessinent, gradients, masques, filtres, motifs, animation le long d'un chemin — six techniques natives, illustrées en direct.
stroke-dashoffset — tracé animé
En jouant sur stroke-dasharray et
stroke-dashoffset, on fait « dessiner » un tracé
comme à la main. Parfait pour signatures, logos animés ou
illustrations qui se révèlent au scroll.
<svg class="trace play" viewBox="0 0 300 120" fill="none">
<path pathLength="1"
d="M10,80 C40,10 70,10 90,60 S140,115 165,60 S215,5 250,50 C272,72 288,60 292,38" />
</svg>
<button id="replay">Rejouer le tracé</button>
.trace { width: 300px; }
.trace path {
fill: none; stroke: #c9952a; stroke-width: 3;
stroke-linecap: round; stroke-linejoin: round;
/* pathLength="1" normalise la longueur → on raisonne en 0–1 */
stroke-dasharray: 1;
stroke-dashoffset: 1; /* tracé masqué */
}
.trace.play path { animation: draw 1.8s ease forwards; }
@keyframes draw { to { stroke-dashoffset: 0; } } /* tracé révélé */
const svg = document.querySelector('.trace');
document.getElementById('replay').addEventListener('click', () => {
svg.classList.remove('play');
void svg.getBoundingClientRect(); // force un reflow → relance l'animation
svg.classList.add('play');
});
Gradients SVG
linearGradient et radialGradient
offrent un contrôle bien plus fin que le CSS : plusieurs stops,
positions précises, et animation des couleurs via SMIL
(<animate>) — sans une ligne de JS.
<svg viewBox="0 0 320 120">
<defs>
<linearGradient id="lin" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#C9952A" />
<stop offset="1" stop-color="#4577e8" />
</linearGradient>
<radialGradient id="rad" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#f7c66b" />
<stop offset="1" stop-color="#1B2A4A" />
</radialGradient>
<!-- couleurs animées via SMIL, sans JS -->
<linearGradient id="anim" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#C9952A">
<animate attributeName="stop-color"
values="#C9952A;#4577e8;#c586c0;#C9952A" dur="6s" repeatCount="indefinite" />
</stop>
<stop offset="1" stop-color="#4577e8">
<animate attributeName="stop-color"
values="#4577e8;#c586c0;#C9952A;#4577e8" dur="6s" repeatCount="indefinite" />
</stop>
</linearGradient>
</defs>
<rect x="0" y="0" width="96" height="120" rx="12" fill="url(#lin)" />
<circle cx="160" cy="60" r="52" fill="url(#rad)" />
<rect x="224" y="0" width="96" height="120" rx="12" fill="url(#anim)" />
</svg>
clipPath & mask
clipPath découpe selon une forme (contour net) ;
mask module l'opacité selon la luminosité (un
dégradé blanc→noir crée un fondu). Deux outils complémentaires
pour habiller images et blocs.
<svg viewBox="0 0 320 140">
<defs>
<clipPath id="clip"><circle cx="75" cy="70" r="62" /></clipPath>
<linearGradient id="rainbow" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#C9952A" />
<stop offset="0.5" stop-color="#c586c0" />
<stop offset="1" stop-color="#4577e8" />
</linearGradient>
<linearGradient id="fade" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#fff" /> <!-- blanc = visible -->
<stop offset="1" stop-color="#000" /> <!-- noir = transparent -->
</linearGradient>
<mask id="mask"><rect width="150" height="100" fill="url(#fade)" /></mask>
</defs>
<!-- clipPath : découpe nette selon la forme -->
<rect x="13" y="8" width="124" height="124" fill="url(#rainbow)" clip-path="url(#clip)" />
<!-- mask : opacité modulée par la luminosité → fondu -->
<g transform="translate(170,20)">
<rect width="150" height="100" rx="12" fill="url(#rainbow)" mask="url(#mask)" />
</g>
</svg>
pattern — motifs répétés
Un <pattern> définit une tuile réutilisée en
remplissage. Points, hachures, textures… le tout vectoriel,
net à toute résolution, et pilotable via
patternTransform.
<svg viewBox="0 0 320 120">
<defs>
<pattern id="dots" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="10" cy="10" r="3" fill="#C9952A" />
</pattern>
<!-- patternTransform incline toute la tuile -->
<pattern id="lines" width="16" height="16" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" y1="0" x2="0" y2="16" stroke="#4577e8" stroke-width="4" />
</pattern>
</defs>
<rect x="0" y="0" width="150" height="120" rx="12" fill="url(#dots)" stroke="#2a3f60" />
<rect x="170" y="0" width="150" height="120" rx="12" fill="url(#lines)" stroke="#2a3f60" />
</svg>
Filtres SVG — glow & goo
Les filtres composables (feGaussianBlur,
feColorMatrix, feMerge) produisent des
effets impossibles en CSS pur : halo lumineux, et le fameux
effet « goo » où des formes fusionnent comme du liquide.
<svg viewBox="0 0 320 140">
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" />
<!-- alpha très contrasté → les bords flous se soudent -->
<feColorMatrix in="blur" type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" />
</filter>
<filter id="glow" x="-60%" y="-60%" width="220%" height="220%">
<feGaussianBlur stdDeviation="4" result="b" />
<feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
</defs>
<g filter="url(#goo)" fill="#C9952A">
<circle cx="70" cy="70" r="20" />
<circle class="goo-mover" cx="120" cy="70" r="20" />
</g>
<text x="200" y="82" filter="url(#glow)" fill="#f7c66b" font-size="34">glow</text>
</svg>
/* le 2e cercle fait des va-et-vient → il fusionne avec le 1er (effet goo) */
.goo-mover { animation: goo 2.8s ease-in-out infinite alternate; }
@keyframes goo {
from { transform: translateX(0); }
to { transform: translateX(-32px); }
}
offset-path — animer le long d'un tracé
offset-path fait suivre à un élément un chemin
arbitraire, offset-distance le fait avancer de 0 à
100 %. Placé dans le SVG, le mobile reste parfaitement aligné
sur le tracé, quelle que soit la taille d'affichage.
<svg viewBox="0 0 320 120">
<path d="M20,60 C80,-10 120,130 180,60 S280,15 300,60"
fill="none" stroke="#2a3f60" stroke-width="2" stroke-dasharray="4 6" />
<circle class="mover" r="8" fill="#C9952A" />
</svg>
<button id="toggle">Pause</button>
.mover {
/* le mobile suit exactement ce tracé (le même que le <path>) */
offset-path: path("M20,60 C80,-10 120,130 180,60 S280,15 300,60");
offset-distance: 0%;
animation: travel 3s linear infinite;
}
@keyframes travel { to { offset-distance: 100%; } }
svg.paused .mover { animation-play-state: paused; }
const svg = document.querySelector('svg');
const btn = document.getElementById('toggle');
btn.addEventListener('click', () => {
const paused = svg.classList.toggle('paused');
btn.textContent = paused ? 'Lecture' : 'Pause';
});