Reputation: 1
Lets say I have an svg,
<svg viewBox="188.1601 295.929 33.6399 36" width="33.6399" height="36" xmlns="http://www.w3.org/2000/svg">
<path fill="#744EAA" d="M 192.8 299.929 C 195.8 299.929 197.8 301.929 200.8 305.929 C 203.8 309.929 208.757 313.12 212.8 313.929 C 217.8 314.929 221.8 318.929 221.8 324.929 C 221.8 329.826 217.954 331.929 212.8 331.929 C 207.8 331.929 203.8 328.929 198.8 323.929 C 193.8 318.929 188.8 309.929 188.8 305.929 C 188.8 301.929 189.8 299.929 192.8 299.929 Z" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 0)"/>
<path fill="#77B255" d="M 190.315 295.929 C 191.563 295.929 191.563 297.177 191.563 298.424 C 191.563 300.188 192.811 299.553 194.059 299.553 C 195.305 299.553 197.8 301.929 197.8 301.929 L 194.058 301.929 C 192.81 301.929 194.058 304.543 192.81 304.543 C 191.562 304.543 191.562 303.355 190.315 303.355 C 189.068 303.355 188.8 306.929 188.8 306.929 C 188.8 306.929 187.196 302.776 189.067 300.905 C 190.315 299.657 187.82 295.929 190.315 295.929 Z" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 0)"/>
</svg>
and I want to convert it to ctx commands so it can be rendered on a 2D Canvas game. Here is an example of what I have:
// body
ctx.fillStyle = '#ffe763';
ctx.strokeStyle = '#a29442';
ctx.beginPath();
const pos = offset(this.renderX, this.renderY);
ctx.arc(pos.x, pos.y, this.r, 0, Math.PI * 2);
ctx.stroke();
ctx.fill();
ctx.closePath();
I tried to find an online converter but they just rendered something at the top left of my screen in low quality.
Upvotes: -1
Views: 59
Reputation: 23397
To implement a full-blow SVG renderer in canvas is quite some work... But if you're in control of the SVG exports you might be able to get away with a very limited implementation like this one:
ctx.scale
and ctx.translate
to implement viewBox
. This ensures we can render our canvas at any resolution without having to worry about the unit system of the SVG drawingPath2D
to construct a path based on the SVG d
attributeI've put some of the limitations of this approach in comments, but just to be sure I'll list them here as well:
path
elements. If you need stuff like <circle />
, <rect />
, etc. you'll have to implement those yourself.fill
attributes directly set to path elements.transform
on path
(al though this one is not hard to add)All in all I think I would recommend an approach where you render svg
images to image data and put that in your canvas instead...
const W = 400;
const H = 400;
const svgString = `<svg viewBox="188.1601 295.929 33.6399 36" width="33.6399" height="36" xmlns="http://www.w3.org/2000/svg">
<path fill="#744EAA" d="M 192.8 299.929 C 195.8 299.929 197.8 301.929 200.8 305.929 C 203.8 309.929 208.757 313.12 212.8 313.929 C 217.8 314.929 221.8 318.929 221.8 324.929 C 221.8 329.826 217.954 331.929 212.8 331.929 C 207.8 331.929 203.8 328.929 198.8 323.929 C 193.8 318.929 188.8 309.929 188.8 305.929 C 188.8 301.929 189.8 299.929 192.8 299.929 Z" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 0)"/>
<path fill="#77B255" d="M 190.315 295.929 C 191.563 295.929 191.563 297.177 191.563 298.424 C 191.563 300.188 192.811 299.553 194.059 299.553 C 195.305 299.553 197.8 301.929 197.8 301.929 L 194.058 301.929 C 192.81 301.929 194.058 304.543 192.81 304.543 C 191.562 304.543 191.562 303.355 190.315 303.355 C 189.068 303.355 188.8 306.929 188.8 306.929 C 188.8 306.929 187.196 302.776 189.067 300.905 C 190.315 299.657 187.82 295.929 190.315 295.929 Z" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 0)"/>
</svg>`;
const img = document.createElement("img");
img.addEventListener("load", e => {
draw(W, H);
});
img.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;
document.body.appendChild(img);
const cvs = document.querySelector("canvas");
const ctx = cvs.getContext("2d");
const draw = (width, height) => {
cvs.width = width;
cvs.height = height;
ctx.drawImage(img, 0, 0, width, height);
}
img,
canvas {
background: #efefef;
}
h2 {
margin: 0;
}
<h2>Canvas</h2>
<canvas></canvas>
<br/>
<button onclick="draw(100, 100)">100⨯100px</button>
<button onclick="draw(200, 200)">200⨯200px</button>
<button onclick="draw(400, 400)">400⨯400px</button>
<h2>Svg source</h2>
const W = 400;
const H = 400;
const svg = document.querySelector("svg");
const cvs = document.querySelector("canvas");
const ctx = cvs.getContext("2d");
const draw = (width, height) => {
// Viewbox
const { width: vbw, height: vbh, x, y } = svg.viewBox.baseVal;
cvs.width = width;
cvs.height = height;
ctx.scale(width / vbw, height / vbh);
ctx.translate(-x, -y);
// Shortcut: only implement <path />
for (const path of svg.querySelectorAll("path")) {
// Shortcut: only support direct fill attributes
// (e.g. no inheritance from parent <g>)
const fill = path.getAttribute("fill");
const d = path.getAttribute("d");
// Shortcut: skip transform attribute
const p = new Path2D(d);
ctx.fillStyle = fill;
ctx.fill(p);
}
}
draw(W, H);
svg,
canvas {
background: #efefef;
}
h2 {
margin: 0;
}
<h2>SVG</h2>
<svg viewBox="188.1601 295.929 33.6399 36" width="33.6399" height="36" xmlns="http://www.w3.org/2000/svg">
<path fill="#744EAA" d="M 192.8 299.929 C 195.8 299.929 197.8 301.929 200.8 305.929 C 203.8 309.929 208.757 313.12 212.8 313.929 C 217.8 314.929 221.8 318.929 221.8 324.929 C 221.8 329.826 217.954 331.929 212.8 331.929 C 207.8 331.929 203.8 328.929 198.8 323.929 C 193.8 318.929 188.8 309.929 188.8 305.929 C 188.8 301.929 189.8 299.929 192.8 299.929 Z" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 0)"/>
<path fill="#77B255" d="M 190.315 295.929 C 191.563 295.929 191.563 297.177 191.563 298.424 C 191.563 300.188 192.811 299.553 194.059 299.553 C 195.305 299.553 197.8 301.929 197.8 301.929 L 194.058 301.929 C 192.81 301.929 194.058 304.543 192.81 304.543 C 191.562 304.543 191.562 303.355 190.315 303.355 C 189.068 303.355 188.8 306.929 188.8 306.929 C 188.8 306.929 187.196 302.776 189.067 300.905 C 190.315 299.657 187.82 295.929 190.315 295.929 Z" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 0)"/>
</svg>
<h2>Canvas</h2>
<canvas></canvas>
<br/>
<button onclick="draw(100, 100)">100⨯100px</button>
<button onclick="draw(200, 200)">200⨯200px</button>
<button onclick="draw(400, 400)">400⨯400px</button>
Upvotes: 0