canvas 内附加到对象的事件

Events attached to object inside canvas

我有简单的 canvas 代码在 canvas

上绘制矩形
var x=document.getElementById("canvas");
var ctx=x.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();

是否可以在所述矩形上添加 eventListener?例如,如果我点击矩形,它会变成红色。

形状和路径作为副作用绘制到 canvas,因此没有要添加事件侦听器的元素;但是,您可以将事件侦听器添加到整个 canvas 或与 canvas 共享位置的元素,然后在单击它时用矩形重新绘制 canvas,但红色(或其他任何改变)。 (确保在使用 .clearRect() 方法重新绘制之前清除 canvas)。

如果你在 canvas 上画东西,绘制的形状不是 javascript 对象,而是改变 canvas 所在的特定状态。因此,您不能将事件侦听器附加到它,而应该将事件附加到 canvas 本身。

然后您的 javascript 可以检查点击的坐标,并确定它是否在矩形内。请记住,如果您在矩形或形状的顶部绘制一些东西,则必须调整代码以检查形成的新区域。如果不是矩形,您可能还会发现很难检查该区域,但仍然可以。

如果你想重绘矩形为红色,你应该重绘canvas,改变你重绘的新矩形的颜色(矩形不是对象,所以你不能直接改变颜色).这还涉及重新绘制 canvas.

上的所有其他形状

地区

根据您想支持各种旧浏览器的程度,您可以通过 Firefox 中的标志启用它来使用 addHitRegion() 和 Chrome(在撰写本文时):

Firefox: about:config -> search "hitregions" and set to true
Chrome: chrome://flags/ -> Enable experimental canvas features

这是唯一直接与事件系统集成的技术。不过,我还不建议将它用于生产环境,据我所知,它也没有 polyfill——但为了展示它的易用性:

var x=document.getElementById("canvas");
var ctx=x.getContext("2d");
ctx.rect(20,20,150,100);
ctx.addHitRegion({id: "demo"});   // enable in flags in Chrome/Firefox
ctx.stroke();

x.addEventListener("click", function(e) {
  if (e.region && e.region === "demo") alert("Hit!");
})
<canvas id="canvas"></canvas>

路径:isPointInPath

其他技术需要手动实现命中检测机制。一种是使用 isPointInPath()。您只需重建要测试的路径,一个接一个,然后 运行 您的(调整后的)x/y 鼠标坐标:

var x=document.getElementById("canvas");
var ctx=x.getContext("2d");
generatePath();
ctx.stroke();

x.addEventListener("click", function(e) {
  var r = this.getBoundingClientRect(),
      x = e.clientX - r.left,
      y = e.clientY - r.top;
  
  // normally you would loop through your paths:
  generatePath();
  if (ctx.isPointInPath(x, y)) alert("Hit!");
})

function generatePath() {
  ctx.beginPath();          // reset path
  ctx.rect(20,20,150,100);  // add region to draw/test
}
<canvas id="canvas"></canvas>

路径:Path2D 对象

对于后一个示例,还有新的 Path2D 对象可以自己保存路径 - 这里的优点是您不需要重建路径,只需传入路径对象使用 x/y 到 isPointInPath() 方法。

问题是 Path2D 是 not supported in all browsers yet, but there is this polyfill 可以为您解决这个问题,

var x=document.getElementById("canvas");
var ctx=x.getContext("2d");

var path1 = new Path2D();
path1.rect(20,20,150,100);  // add rect to path object

ctx.stroke(path1);

x.addEventListener("click", function(e) {
  var r = this.getBoundingClientRect(),
      x = e.clientX - r.left,
      y = e.clientY - r.top;
  
  // normally you would loop through your paths objects:
  if (ctx.isPointInPath(path1, x, y)) alert("Hit!");
})
<canvas id="canvas"></canvas>

手动检查边界

当然,还有使用手动边界检查的旧技术。这将适用于所有浏览器。在这里,明智的做法是创建包含边界并可用于渲染边界的对象。这通常会将您限制在矩形区域 - 更复杂的形状将需要更复杂的算法(例如 isPointInPath() 嵌入)。

var x=document.getElementById("canvas");
var ctx=x.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();

x.addEventListener("click", function(e) {
  var r = this.getBoundingClientRect(),
      x = e.clientX - r.left,
      y = e.clientY - r.top;
  
  // normally you would loop through your region objects:
  if (x >= 20 && x < 20+150 && y >= 20 && y < 20+100) alert("Hit!");
})
<canvas id="canvas"></canvas>