关于将 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 的影响)。