PerfectVector
Par Irene Kim10 min de lecture

SVG Canvas Path2D : réutiliser les tracés à la bonne taille

Réutilise les tracés SVG sur canvas avec la bonne transformation viewBox, les couleurs, trous et densité de pixels. Suis l’exemple et garde l’original SVG éditable.

Sur cette page

Transmets l’attribut d d’un tracé à new Path2D(d), puis dessine-le avec un contexte canvas. Pour reproduire le SVG, reprends aussi sa correspondance de coordonnées, son rendu et sa règle de remplissage. Le constructeur accepte des données de tracé ; il n’importe pas un document SVG et n’applique pas son viewBox, son CSS ou ses transformations de groupes (constructeur Path2D sur MDN).

Cela compte lorsqu’une icône est correcte en SVG, mais devient petite, décalée, noire ou pleine dans son ouverture sur canvas. L’exemple ci-dessous corrige ces différences avec deux tracés et une transformation d’ajustement explicite. Pars d’une géométrie vectorielle existante si tu en as une. La vectorisation n’est utile que lorsque le dessin à réutiliser existe encore en pixels.

Ce que contient un objet Path2D

Considère les données de tracé comme la description des contours. Les autres instructions SVG existent hors de cette chaîne et ton code de dessin canvas doit fournir celles dont il a besoin.

Entrée SVGResponsabilité côté canvas
Valeur d d’un tracéConstruire la géométrie Path2D
viewBox et taille affichéeCalculer la transformation de dessin
fill et fill-ruleDéfinir fillStyle et transmettre la règle à fill()
transform du parent ou du tracéAppliquer séparément la transformation équivalente
CSS, dégradés, masques, texte et imagesImplémenter le rendu nécessaire ou choisir une autre méthode

L’exemple utilise des remplissages littéraux, deux tracés enfants directs et aucune autre fonctionnalité SVG. Il est volontairement assez petit pour être examiné. Copier sa boucle d’extraction dans un importateur SVG général ferait perdre les styles hérités et les éléments non pris en charge.

Comparer la source à un canvas non configuré

Cet emblème original comporte un corps arrondi bleu-vert, une pointe orange et une ouverture carrée. Son viewBox commence à (20, 10), pas à (0, 0). Le sous-tracé intérieur suit le même sens que la limite extérieure ; la règle déclarée evenodd compte donc.

Place ce SVG et un canvas dans une page HTML :

<svg id="source" xmlns="http://www.w3.org/2000/svg"
     viewBox="20 10 120 80" width="240" height="160">
  <path fill="#126d70" fill-rule="evenodd"
        d="M40 20 H90 Q110 20 110 40 V60 Q110 80 90 80 H40 Q30 80 30 70 V30 Q30 20 40 20 Z M50 35 H80 V60 H50 Z"/>
  <path fill="#f19b38" d="M115 25 L135 50 L115 75 Z"/>
</svg>
<canvas id="result"></canvas>

Si tu parcours les deux valeurs d et appelles ctx.fill(new Path2D(d)) sans rien régler d’autre, la géométrie utilise les coordonnées canvas, le rendu et la règle de remplissage par défaut. Elle ne reprend pas automatiquement l’apparence du SVG. La méthode canvas fill() accepte un tracé et une règle explicite evenodd ou nonzero ; nonzero est la valeur par défaut (fill() sur MDN).

Comparaison navigateur d’un emblème SVG, d’un petit rendu Path2D brut noir à ouverture remplie et d’un rendu canvas bicolore correctement ajusté
Les mêmes tracés écrits à la main dans un navigateur : référence SVG, Path2D brut et ajustement explicite avec couleurs et evenodd restaurés. Le tableau indique les dimensions du bitmap pour quatre entrées de taille et DPR.

Méthode : nous avons rendu ce SVG écrit à la main dans le navigateur, dessiné ses deux tracés avec les deux méthodes canvas et réexécuté le code publié dans une page HTML séparée. La capture est la sortie réelle du navigateur. Elle démontre la gestion des coordonnées et du rendu, pas une conversion PerfectVector.

Faire correspondre le viewBox au canvas

Le comportement SVG par défaut préservant les proportions ajuste le viewBox dans sa zone d’affichage et le centre. La spécification du système de coordonnées SVG définit cette correspondance. Pour cet exemple sans hiérarchie, reproduis xMidYMid meet avec une échelle uniforme et une translation :

scale = min(cssWidth / viewBoxWidth, cssHeight / viewBoxHeight)
tx = (cssWidth - viewBoxWidth * scale) / 2 - viewBoxX * scale
ty = (cssHeight - viewBoxHeight * scale) / 2 - viewBoxY * scale

Les derniers termes retirent l’origine non nulle. À 240 × 160, l’échelle est 2, tx vaut -40 et ty vaut -20. À 300 × 120, l’échelle devient 1.5 ; le viewBox ajusté de 180 pixels de large dispose de 60 pixels d’espace horizontal de chaque côté. La translation est donc (30, -15) après prise en compte de l’origine source.

Utilise le script suivant après le HTML ci-dessus. Il gère les attributs directs des tracés de cet exemple, redessine tout le canvas et distingue taille CSS et taille du bitmap sous-jacent :

const source = document.querySelector("#source");
const vb = source.viewBox.baseVal;
const layers = [...source.querySelectorAll("path")].map(el => ({
  path: new Path2D(el.getAttribute("d")),
  fill: el.getAttribute("fill"),
  rule: el.getAttribute("fill-rule") || "nonzero"
}));
 
function draw(canvas, cssWidth, cssHeight, dpr = window.devicePixelRatio || 1) {
  canvas.style.width = `${cssWidth}px`;
  canvas.style.height = `${cssHeight}px`;
  canvas.width = Math.round(cssWidth * dpr);
  canvas.height = Math.round(cssHeight * dpr);
  const ctx = canvas.getContext("2d");
  const scale = Math.min(cssWidth / vb.width, cssHeight / vb.height);
  const tx = (cssWidth - vb.width * scale) / 2 - vb.x * scale;
  const ty = (cssHeight - vb.height * scale) / 2 - vb.y * scale;
  const px = canvas.width / cssWidth;
  const py = canvas.height / cssHeight;
  ctx.setTransform(px * scale, 0, 0, py * scale, px * tx, py * ty);
  for (const layer of layers) {
    ctx.fillStyle = layer.fill;
    ctx.fill(layer.path, layer.rule);
  }
}
 
draw(document.querySelector("#result"), 240, 160);
 

Définir canvas.width et canvas.height efface et réinitialise le contexte. La fonction définit ensuite toute la transformation et repeint chaque couche ; les appels répétés n’accumulent donc pas les échelles. Elle suppose un viewBox valide de largeur et hauteur positives et des dimensions demandées positives.

px et py utilisent les dimensions réelles arrondies du bitmap. Les coordonnées CSS restent ainsi correctement associées aux pixels alloués même lorsque la densité demandée produit des dimensions fractionnaires. L’exemple MDN de devicePixelRatio explique pourquoi la taille affichée du canvas et ses dimensions en pixels doivent être traitées séparément.

Vérifier séparément taille et densité de pixels

Nous avons transmis explicitement les valeurs DPR 1 et 2 à la même fonction pour deux tailles CSS. Il s’agit d’entrées de densité contrôlées, pas d’une affirmation que nous avons testé deux appareils physiques. Le rapport de pixels indiqué par le navigateur pour l’appel par défaut était 2.

Taille CSSArgument DPRBitmap canvas observé
240 × 1601240 × 160
240 × 1602480 × 320
300 × 1201300 × 120
300 × 1202600 × 240

Le centre de l’ouverture est resté transparent dans les quatre cas. Une densité supérieure change la résolution du bitmap ; elle ne corrige pas une mauvaise transformation ni une règle de remplissage manquante. Redessine lorsque les dimensions CSS prévues ou le rapport de pixels de l’appareil changent. Un agrandissement uniquement CSS étire le bitmap déjà présent sur le canvas.

Si la source est déjà incorrecte avant d’atteindre canvas, utilise les vérifications de dimensions et viewBox SVG. Si seul canvas est incorrect, compare d’abord sa transformation et sa gestion du rendu à la référence SVG fonctionnelle.

Préserver la source et choisir la bonne méthode d’import

Garde l’original SVG et ses attributs avec les données de tracé dérivées. Une routine de dessin Path2D est utile lorsque tu contrôles un petit ensemble de contours et leurs styles. Pour un SVG complexe dont seule l’apparence rendue par le navigateur t’intéresse, le charger comme image et dessiner cette image peut éviter de reconstruire toutes ses fonctionnalités. Cette méthode peint toujours dans le bitmap canvas.

Enregistrer la sortie canvas en PNG crée une image matricielle, même si le dessin a commencé avec des tracés vectoriels. L’API d’export canvas produit un blob d’image ; la prise en charge du PNG est requise (toBlob() sur MDN). Garde le SVG pour modifier les tracés plus tard. De même, un fichier SVG peut contenir une image matricielle plutôt que des données de tracé utiles ; le diagnostic des images intégrées aide à distinguer ces cas.

PerfectVector convient lorsqu’il ne reste qu’un PNG ou JPG de ton icône et que tu as besoin de contours réutilisables. Convertis cette image en SVG, puis examine sa silhouette, ses couleurs séparées et ses petites ouvertures avant d’extraire les tracés pour canvas. Un original SVG propre doit passer directement à l’étape de préparation. La vectorisation n’implémente pas la correspondance viewBox, ne copie pas le CSS et ne rend pas un PNG canvas éditable en vecteurs.

Pour une famille complète d’icônes, établis les règles communes de grille et de couleurs du kit d’interface avant de connecter chaque routine de dessin. Tu trouveras d’autres exemples de préparation SVG dans le blog.

Avant d’utiliser le dessin dans ton interface

  • Vérifie la référence SVG aux mêmes dimensions d’affichage que le canvas.
  • Confirme l’origine du viewBox ainsi que sa largeur et sa hauteur.
  • Transfère les couleurs et la règle de remplissage prévue ; examine chaque ouverture.
  • Tiens compte des transformations de groupes au lieu de copier seulement d.
  • Lis les canvas.width et canvas.height réels, puis teste la plus grande taille d’affichage prévue.
  • Préserve l’original SVG et fournis un nom accessible ou un texte environnant adapté à l’interface.

FAQ

Path2D peut-il importer tout un fichier SVG ? Non. Son entrée chaîne est constituée de données de tracé SVG, comme la valeur de l’attribut d d’un tracé. La structure du document, la correspondance viewBox, les styles et les transformations de groupes nécessitent une gestion distincte.

Pourquoi le trou de mon SVG s’est-il rempli sur canvas ? Vérifie la règle de remplissage et le sens des sous-tracés. Cet exemple nécessite evenodd, alors que fill de canvas utilise nonzero par défaut. Transmets la règle prévue lors du remplissage de l’objet Path2D.

Dessiner des tracés SVG sur canvas préserve-t-il un export vectoriel ? Les tracés restent des instructions de dessin réutilisables dans ton programme, mais exporter canvas en PNG produit des pixels. Garde le SVG original pour l’édition et la livraison vectorielle.

Sources

  1. MDN — Constructeur Path2D — Définit la chaîne de données de tracé acceptée par le constructeur.
  2. W3C — Systèmes de coordonnées SVG — Définit l’origine du viewBox, la correspondance de zone d’affichage et l’alignement préservant les proportions.
  3. MDN — fill() de Canvas — Décrit le remplissage de tracés et les règles evenodd et nonzero.
  4. MDN — devicePixelRatio — Explique la taille CSS affichée et la résolution sous-jacente du canvas.
  5. MDN — toBlob() de Canvas — Décrit l’export d’une image matricielle depuis un canvas.

Tu n’as qu’une icône matricielle ? Prépare son SVG avec PerfectVector, vérifie le contour et les ouvertures, puis compare le dessin canvas à ce SVG aux tailles réelles de ton interface.

Plus depuis le blog

Repartez d’un SVG plus net
et plus facile à modifier