关于将 SVG 路径绘制到 canvas 中的建议
Advise on drawing an SVG path onto a canvas
我有以下 SVG 元素:
<svg
viewBox="0 0 1000 1000"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
aria-label="The Moon"
role="img"
unselectable="on"
tabindex="-1"
draggable="false"
class="absolute -z-10 top-full right-full"
data-phase="0.9033397192448547"
data-age="26.676153687326483"
>
<path d="m500,0 a10,10 0 1,1 0,1000 a10,10 0 1,1 0,-1000" fill="rgba(255, 255, 255)"></path>
<path d="m500,0 a10,10 0 1,1 0,1000 a10,10 0 1,1 0,-1000" fill="rgba(31, 41, 55, 0.60)"></path>
<path d="m500,0 a 6.133588769794187,10 0 1, 0 0,1000 a10,10 0 1, 1 0,-1000" fill="#fff"></path>
</svg>
但是,我想知道...可以使用以下方法绘制这些路径:
ctx.beginPath()
const p = new Path2D(
`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`
)
p.moveTo(x - radius / 2, y - radius / 2)
ctx.stroke(p)
ctx.fill(p)
ctx.closePath()
但是,它似乎不想在我指定的 [x,y] 位置绘制路径,它只是将它放在我的 canvas 的左上角.. .
我似乎无法参考如何在特定的 x 和 y 坐标处放置 2D 路径...
我也试过这个,但结果同样令人沮丧:
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const p = new Path2D()
p.moveTo(x - radius / 2, y - radius / 2)
p.addPath(
new Path2D(
`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`
)
)
ctx.stroke(p)
ctx.fill(p)
ctx.closePath()
任何指路明灯都会很棒!
你的路径声明的相关命令不会像你预期的那样工作。
它们将在创建内部 Path2D 时被解析,它还没有终点,因此它们都将相对于初始 0,0
点。
一旦创建了这个内部 Path2D,声明使用相对方法的事实将丢失,所有这些点都在内部转换为绝对坐标。因此,您调用 addPath(path)
的最终 Path2D 的最后一点无关紧要。
做你想做的事,你有几个解决方案:
- 您可以在路径声明前加上
Mx,y
:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p = new Path2D(`M${x - radius / 2},${y - radius / 2}m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
ctx.stroke(p)
ctx.fill(p)
body { background: #333; }
<canvas></canvas>
通过这种方式,您甚至可以删除以下 m
并将其合并到 M
计算中:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p = new Path2D(`M${x - radius / 2 + radius},${y - radius / 2}a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
ctx.stroke(p)
ctx.fill(p)
body { background: #333; }
<canvas></canvas>
或者您可以使用上下文变换矩阵 (CTM) 移动实际绘制路径的位置:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p = new Path2D(`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
// set x, y through CTM
ctx.translate(x - radius / 2, y - radius / 2);
ctx.stroke(p)
ctx.fill(p)
// reset CTM to defaults
ctx.setTransform(1, 0, 0, 1, 0, 0);
body { background: #333; }
<canvas></canvas>
或者你可以使用Path2D.addPath(path, matrix)
的matrix
参数直接翻译路径:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p1 = new Path2D(`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
const p = new Path2D();
const matrix = { e: x - radius / 2 , f: y - radius / 2 };
p.addPath(p1, matrix);
ctx.stroke(p);
ctx.fill(p);
body { background: #333; }
<canvas></canvas>
这些解决方案中的每一个都有自己的优势,第一个可能更容易阅读,如果您想在每一帧更改 Path2D 的位置,CTM 最好,最后一个最好,如果您需要用图案或渐变填充 Path2D(这也会受到 CTM 的影响)。
我有以下 SVG 元素:
<svg
viewBox="0 0 1000 1000"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
aria-label="The Moon"
role="img"
unselectable="on"
tabindex="-1"
draggable="false"
class="absolute -z-10 top-full right-full"
data-phase="0.9033397192448547"
data-age="26.676153687326483"
>
<path d="m500,0 a10,10 0 1,1 0,1000 a10,10 0 1,1 0,-1000" fill="rgba(255, 255, 255)"></path>
<path d="m500,0 a10,10 0 1,1 0,1000 a10,10 0 1,1 0,-1000" fill="rgba(31, 41, 55, 0.60)"></path>
<path d="m500,0 a 6.133588769794187,10 0 1, 0 0,1000 a10,10 0 1, 1 0,-1000" fill="#fff"></path>
</svg>
但是,我想知道...可以使用以下方法绘制这些路径:
ctx.beginPath()
const p = new Path2D(
`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`
)
p.moveTo(x - radius / 2, y - radius / 2)
ctx.stroke(p)
ctx.fill(p)
ctx.closePath()
但是,它似乎不想在我指定的 [x,y] 位置绘制路径,它只是将它放在我的 canvas 的左上角.. .
我似乎无法参考如何在特定的 x 和 y 坐标处放置 2D 路径...
我也试过这个,但结果同样令人沮丧:
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const p = new Path2D()
p.moveTo(x - radius / 2, y - radius / 2)
p.addPath(
new Path2D(
`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`
)
)
ctx.stroke(p)
ctx.fill(p)
ctx.closePath()
任何指路明灯都会很棒!
你的路径声明的相关命令不会像你预期的那样工作。
它们将在创建内部 Path2D 时被解析,它还没有终点,因此它们都将相对于初始 0,0
点。
一旦创建了这个内部 Path2D,声明使用相对方法的事实将丢失,所有这些点都在内部转换为绝对坐标。因此,您调用 addPath(path)
的最终 Path2D 的最后一点无关紧要。
做你想做的事,你有几个解决方案:
- 您可以在路径声明前加上
Mx,y
:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p = new Path2D(`M${x - radius / 2},${y - radius / 2}m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
ctx.stroke(p)
ctx.fill(p)
body { background: #333; }
<canvas></canvas>
通过这种方式,您甚至可以删除以下 m
并将其合并到 M
计算中:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p = new Path2D(`M${x - radius / 2 + radius},${y - radius / 2}a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
ctx.stroke(p)
ctx.fill(p)
body { background: #333; }
<canvas></canvas>
或者您可以使用上下文变换矩阵 (CTM) 移动实际绘制路径的位置:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p = new Path2D(`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
// set x, y through CTM
ctx.translate(x - radius / 2, y - radius / 2);
ctx.stroke(p)
ctx.fill(p)
// reset CTM to defaults
ctx.setTransform(1, 0, 0, 1, 0, 0);
body { background: #333; }
<canvas></canvas>
或者你可以使用Path2D.addPath(path, matrix)
的matrix
参数直接翻译路径:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = '#fff'
ctx.lineWidth = 1
ctx.fillStyle = '#fff'
const radius = 50;
const x = 150;
const y = 70;
const p1 = new Path2D(`m${radius},0 a 6.133588769794187,10 0 1, 0 0,${radius * 2} a10,10 0 1, 1 0,${radius * -2}`);
const p = new Path2D();
const matrix = { e: x - radius / 2 , f: y - radius / 2 };
p.addPath(p1, matrix);
ctx.stroke(p);
ctx.fill(p);
body { background: #333; }
<canvas></canvas>
这些解决方案中的每一个都有自己的优势,第一个可能更容易阅读,如果您想在每一帧更改 Path2D 的位置,CTM 最好,最后一个最好,如果您需要用图案或渐变填充 Path2D(这也会受到 CTM 的影响)。