2
  1. I suppose this doesn't work because canvas is drawing a bitmap of a vector (and a bitmap is not a path).
  2. Even if it did work, the bitmap is likely always has a rectangular permitter.

Is there any way to leverage something like isPointInPath when using drawImage?

example:

  • The top canvas is drawn using drawImage and isPointInPath does not work.
  • The bottom canvas is drawn using arc and isPointInPath works.

a link to my proof

** EDIT **

I draw a circle on one canvas, and use isPointInPath to see if the mouse pointer is inside the circle (bottom canvas in my example). I also "copy" the bottom canvas to the top canvas using drawImage. Notice that isPointInPath will not work on the top canvas (most likely due to reasons I mentioned above). Is there a work-around I can use for this that will work for ANY kind of path (or bitmap)?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284

4 Answers4

4

A canvas context has this hidden thing called the current path. ctx.beginPath, ctx.lineTo etc create this path.

When you call ctx.stroke() or ctx.fill() the canvas strokes or fills that path.

Even after it is stroked or filled, the path is still present in the context.

This path is the only thing that isPointInPath tests.

If you want to test if something is in an image you have drawn or a rectangle that was drawn with ctx.fillRect(), that is not possible using built in methods.

Typically you'd want to use a is-point-in-rectangle function that you write yourself (or get from someone else).

If you're looking for how to do pixel-perfect (instead of just the image rectangle) hit detection for an image there are various methods of doing that discussed here: Pixel perfect 2D mouse picking with Canvas

Community
  • 1
  • 1
Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
1

You could try reimplementing ctx.drawImage() to always draw a box behind the image itself, like so (JSFiddle example):

ctx.customDrawImage = function(image, x, y){
    this.drawImage(image, x, y);
    this.rect(x, y, image.width, image.height);
}
var img1 = new Image();
img1.onload = function(){
var x = y = 0;
ctx.drawImage(img1, x, y);
console.log(ctx.isPointInPath(x + 1, y + 1));

x = 1.25 * img1.width;
ctx.customDrawImage(img1, x, y);
console.log(ctx.isPointInPath(x + 1, y + 1));

Note: you might get side effects like the rectangle appearing over the image, or bleeding through from behind if you are not careful.

puk
  • 16,318
  • 29
  • 119
  • 199
  • You could avoid that bleeding by not stroke()ing or fill()ing the path. Just test it and throw it away. – Richard Oct 25 '12 at 07:50
0

To me, isPointInPath failed after canvas was moved. So, I used:

mouseClientX -= gCanvasElement.offsetLeft; mouseclientY -= gCanvasElement.offsetTop;

Gilberto Alexandre
  • 2,227
  • 1
  • 18
  • 20
0

I had some more challenges, because my canvas element could be rescaled. So first when I draw the figures, in my case arc, I save them in an array together with a name and draw them:

      if (this.coInit == false)
      {
        let co = new TempCO ();
        co.name= sensor.Name;
        co.path = new Path2D();
        co.path.arc(c.X, c.Y, this.radius,  0, 2 * Math.PI);
        this.coWithPath.push(co);
      }
      let coWP = this.coWithPath.find(c=>c.name == sensor.Name);
      this.ctx.fillStyle = color;
      this.ctx.fill(coWP.path);

Then in the mouse event, I loop over the items and check if the click event is in a path. But I also need to rescale the mouse coordinates according to the resized canvas:

getCursorPosition(event) {
  const rect = this.ctx.canvas.getBoundingClientRect();
  const x = ((event.clientX - rect.left ) / rect.width) * this.canvasWidth;
  const y = ((event.clientY - rect.top) / rect.height) * this.canvasHeight;
  this.coWithPath.forEach(c=>{

    if (this.ctx.isPointInPath(c.path, x, y))
    {
      console.log("arc is hit", c);
      //Switch light
    }
  });
}

So I get the current size of the canvas and rescale the point to the original size. Now it works!

This is how the TempCO looks like:

export class TempCO
{
  path : Path2D;
  name : string;
}
Mark
  • 379
  • 3
  • 7