PerfectVector
Por Irene Kim10 min de leitura

SVG com Path2D no canvas: reutilize traçados no tamanho certo

Reutilize dados de traçados SVG no canvas com transformação de viewBox, cores, vazados e densidade corretos. Siga um exemplo original e mantenha o mestre editável.

Nesta página

Passe o atributo d de um traçado para new Path2D(d) e desenhe-o com um contexto de canvas. Para corresponder ao SVG, transfira também seu mapeamento de coordenadas, pintura e regra de preenchimento. O construtor aceita dados de traçado; ele não importa um documento SVG nem aplica o viewBox, o CSS ou as transformações de grupo do documento (construtor Path2D na MDN).

Isso importa quando um ícone personalizado parece correto como SVG, mas fica pequeno, deslocado, preto ou com a abertura preenchida no canvas. O exemplo abaixo corrige essas diferenças com dois traçados e uma transformação explícita de ajuste. Comece com a geometria vetorial existente quando você a tiver. A vetorização só é útil quando a arte que você precisa reutilizar ainda existe como pixels.

O que um objeto Path2D contém

Pense nos dados do traçado como a descrição do contorno. Outras instruções SVG ficam fora dessa string, e seu código de desenho no canvas precisa fornecer as de que necessita.

Entrada SVGResponsabilidade no canvas
Valor d de um traçadoConstruir a geometria Path2D
viewBox e tamanho exibidoCalcular a transformação de desenho
fill e fill-ruleDefinir fillStyle e passar a regra para fill()
transform do pai ou do traçadoAplicar a transformação equivalente separadamente
CSS, gradientes, máscaras, texto e imagensImplementar a renderização necessária ou escolher outra opção

A amostra usa preenchimentos literais, dois traçados filhos diretos e nenhum outro recurso de SVG. Ela é deliberadamente pequena o bastante para ser inspecionada. Copiar seu laço de extração para um importador SVG arbitrário perderia estilos herdados e elementos incompatíveis.

Compare o SVG original com um canvas sem configuração

Este emblema original tem um corpo verde-azulado arredondado, uma ponta laranja e uma abertura quadrada. Seu viewBox começa em (20, 10), não em (0, 0). O subtraçado interno segue a mesma direção do limite externo, por isso a regra declarada evenodd importa.

Coloque este SVG e um canvas em uma página 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>

Se você percorrer os dois valores d e chamar ctx.fill(new Path2D(d)) sem definir mais nada, a geometria usará as coordenadas do canvas e a pintura e a regra de preenchimento padrão. Ela não adquire automaticamente a aparência do SVG. O fill() do canvas aceita tanto um traçado quanto uma regra explícita evenodd ou nonzero; nonzero é o padrão (fill() na MDN).

Comparação no navegador de um emblema SVG, uma renderização Path2D bruta pequena e preta com a abertura preenchida e uma renderização no canvas de duas cores corretamente ajustada
Os mesmos traçados feitos manualmente em um navegador: referência SVG, Path2D bruto e um ajuste explícito com pinturas e evenodd restaurados. A tabela registra as dimensões do bitmap para quatro entradas de tamanho e DPR.

Método: renderizamos este SVG feito manualmente no navegador, desenhamos seus dois traçados pelas duas opções de canvas e executamos novamente o código publicado em uma página HTML separada. A captura de tela é a saída real do navegador. Ela demonstra o tratamento de coordenadas e pintura, não uma conversão do PerfectVector.

Mapeie o viewBox para o canvas

O comportamento padrão do SVG que preserva a proporção ajusta o viewBox dentro de sua área de visualização e o centraliza. A especificação de sistemas de coordenadas SVG define esse mapeamento. Para este exemplo plano, reproduza xMidYMid meet com uma escala uniforme e uma translação:

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

Os últimos termos removem a origem diferente de zero. Em 240 × 160, a escala é 2, tx é -40 e ty é -20. Em 300 × 120, a escala passa a 1.5; o viewBox ajustado com 180 pixels de largura ganha 60 pixels de espaço horizontal em cada lado. A translação é, portanto, (30, -15) após considerar a origem do SVG.

Use o script abaixo depois do HTML acima. Ele trata os atributos diretos dos traçados desta amostra, redesenha todo o canvas e separa o tamanho CSS do tamanho do bitmap subjacente:

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);
 

Definir canvas.width e canvas.height limpa e reinicia o contexto. A função então define a transformação completa e pinta todas as camadas novamente, de modo que chamadas repetidas não acumulem escala. Ela pressupõe um viewBox válido com largura e altura positivas e dimensões solicitadas positivas.

px e py usam as dimensões reais arredondadas do bitmap. Isso mantém as coordenadas CSS mapeadas para os pixels alocados mesmo quando a densidade solicitada produz dimensões fracionárias. O exemplo de devicePixelRatio da MDN explica por que o tamanho de exibição do canvas e as dimensões em pixels precisam de tratamento separado.

Confira o tamanho e a densidade de pixels separadamente

Passamos os valores de DPR 1 e 2 explicitamente à mesma função em dois tamanhos CSS. São entradas de densidade controladas, não uma afirmação de que testamos dois dispositivos físicos. A proporção de pixels do dispositivo informada pelo navegador para a chamada padrão foi 2.

Tamanho CSSArgumento DPRBitmap observado no canvas
240 × 1601240 × 160
240 × 1602480 × 320
300 × 1201300 × 120
300 × 1202600 × 240

O centro da abertura continuou transparente nos quatro casos. Uma densidade maior muda a resolução do bitmap subjacente; ela não corrige uma transformação errada nem recupera uma regra de preenchimento ausente. Redesenhe quando as dimensões CSS pretendidas ou a proporção de pixels do dispositivo mudarem. Uma ampliação feita apenas com CSS estica o bitmap que já está no canvas.

Se o SVG original já está errado antes de chegar ao canvas, use as verificações de tamanho e viewBox do SVG. Se somente o canvas está errado, compare primeiro seu tratamento de transformação e pintura com a referência SVG que funciona.

Preserve o arquivo original e escolha a opção certa de importação

Mantenha o mestre SVG e seus atributos junto de quaisquer dados de traçado derivados. Uma rotina de desenho com Path2D é útil quando você controla um pequeno conjunto de contornos e seus estilos. Para um SVG complexo cuja aparência renderizada pelo navegador é tudo de que você precisa, carregá-lo como imagem e desenhar essa imagem pode evitar reconstruir cada recurso. Essa opção ainda pinta no bitmap do canvas.

Salvar a saída do canvas como PNG cria uma imagem raster, mesmo que o desenho tenha começado com traçados vetoriais. A API de exportação do canvas produz um blob de imagem; o suporte a PNG é obrigatório (toBlob() na MDN). Mantenha o SVG para editar os traçados depois. Da mesma forma, um arquivo SVG pode conter uma imagem raster em vez de dados de traçado úteis; o diagnóstico de imagens incorporadas ajuda a distinguir esses casos.

O PerfectVector é adequado quando resta apenas um PNG ou JPG do seu ícone personalizado e você precisa de contornos reutilizáveis. Converta essa imagem para SVG e inspecione sua silhueta, cores separadas e pequenas aberturas antes de extrair os traçados para o canvas. Um mestre SVG limpo deve ir diretamente para a etapa de preparação. A vetorização não implementa o mapeamento de viewBox, não copia CSS nem torna um PNG do canvas editável como vetores.

Para uma família inteira de ícones, estabeleça a grade compartilhada e as regras de pintura do kit de interface antes de conectar cada rotina de desenho. Há mais exemplos de preparação de SVG no blog.

Antes de usar o desenho na sua interface

  • Confira a referência SVG nas mesmas dimensões de exibição do canvas.
  • Confirme a origem do viewBox, além de sua largura e altura.
  • Transfira as pinturas e a regra de preenchimento pretendida; inspecione todas as aberturas.
  • Considere as transformações de grupo em vez de copiar apenas d.
  • Leia os valores reais de canvas.width e canvas.height e teste o maior tamanho pretendido de exibição.
  • Preserve o mestre SVG e forneça um nome acessível ou texto ao redor adequado à interface.

Perguntas frequentes

Path2D consegue importar um arquivo SVG inteiro? Não. Sua entrada de string são dados de traçado SVG, como o valor do atributo d de um traçado. A estrutura do documento, o mapeamento de viewBox, os estilos e as transformações de grupo precisam de tratamento separado.

Por que o vazado do meu SVG foi preenchido no canvas? Confira a regra de preenchimento e as direções dos subtraçados. Este exemplo exige evenodd, enquanto o preenchimento do canvas usa nonzero por padrão. Passe a regra pretendida ao preencher o objeto Path2D.

Desenhar traçados SVG no canvas preserva uma exportação vetorial? Os traçados continuam sendo instruções de desenho reutilizáveis no seu programa, mas exportar o canvas como PNG produz pixels. Mantenha o SVG original para edição e entrega vetorial.

Fontes

  1. MDN — construtor Path2D — Define a string de dados de traçado aceita pelo construtor.
  2. W3C — sistemas de coordenadas SVG — Define a origem do viewBox, o mapeamento da área de visualização e o alinhamento que preserva a proporção.
  3. MDN — fill() do canvas — Documenta o preenchimento de traçados e as regras evenodd e nonzero.
  4. MDN — devicePixelRatio — Explica o tamanho de exibição CSS e a resolução do bitmap subjacente do canvas.
  5. MDN — toBlob() do canvas — Documenta a exportação de imagens raster a partir de um canvas.

Você só tem um ícone raster? Prepare seu SVG com o PerfectVector, confira o contorno e as aberturas e compare o desenho no canvas com esse SVG nos tamanhos reais da sua interface.

Mais do blog

Recomece com um SVG mais limpo
e fácil de editar