在不求助于外部库的情况下单击复杂 canvas 形状上的事件

Click events on complex canvas shapes without recourse to external libraries

我想在单个 canvas 元素上实现多个复杂形状的点击检测,类似于 CanvasRenderingContext2D.isPointInPath() 实现的那样。

下面的强制性示例代码。

HTML:

<canvas id="canvas"></canvas>
<p>In path: <code id="result">false</code></p>

JS:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const result = document.getElementById('result');

ctx.beginPath();
ctx.moveTo(25, 25);
ctx.lineTo(105, 25);
ctx.lineTo(25, 105);
ctx.fill();

ctx.beginPath();
ctx.moveTo(125, 45);
ctx.lineTo(45, 125);
ctx.lineTo(125, 125);
ctx.lineTo(205, 45);
ctx.closePath();
ctx.fill();

window.addEventListener('mousemove', e => {
result.innerText = `${ctx.isPointInPath(e.clientX, e.clientY)} X: ${e.clientX} Y: ${e.clientY}`;
});

虽然以上方法适用于最后绘制的形状,但我希望能够对之前绘制的任何形状执行相同的检查。

我正在进行的项目涉及在等距地图上选择不同的图块,因此我希望在单击该图块后尽可能多地接收有关所选图块的信息。

由于我打算绘制的形状数量太多,我宁愿不必诉诸渲染 SVG。此外,外部库是不受欢迎的,我犹豫是否为我在 'visible' canvas 上绘制的每个形状绘制伪 canvas 只是为了能够检测点击。除了等待受灾地区退出实验状态之外,还有什么其他选择?

我在这里遇到了一个类似但最终不同的问题:complex shape selection on canvas

isPointInPath accepts a Path2D 对象作为可选的第一个参数。

所以一个简单的方法是为每个形状创建这样的 Path2D 对象。
它甚至可以简化您的绘图操作,因为 fill()stroke() 也接受这些对象:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const result = document.getElementById('result');

const shape1 = new Path2D();
shape1.moveTo(25, 25);
shape1.lineTo(105, 25);
shape1.lineTo(25, 105);

const shape2 = new Path2D();
shape2.moveTo(125, 45);
shape2.lineTo(45, 125);
shape2.lineTo(125, 125);
shape2.lineTo(205, 45);
shape2.closePath();

// to render it
ctx.fill(shape1);
ctx.fill(shape2);

canvas.addEventListener('mousemove', e => {
result.textContent = `
  shape1: ${ctx.isPointInPath(shape1, e.offsetX, e.offsetY)}
  shape2: ${ctx.isPointInPath(shape2, e.offsetX, e.offsetY)}
  X: ${e.offsetX} Y: ${e.offsetY}`;
});
.log { display: inline-block; vertical-align: top}
<canvas id="canvas"></canvas>
<div class="log">In path: <pre id="result">false</pre></div>