2

I have an area on an map which is clipped out! Is it possible to detect at click outside the clipped area, so if people click on the black dimmed area, it will show an alert?

This is my code:

var canvas  = document.getElementById("canvas");
var ctx     = canvas.getContext("2d");
var image   = new Image;

image.addEventListener('load', function(){

  ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

  ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
  ctx.fillRect(0, 0, 870, 500);

  // Use save and restore to make sure the canvas restores its non-clipping path setup after clipping
  ctx.save();
  ctx.beginPath();
  ctx.moveTo(10, 10);
  ctx.lineTo(100,50);
  ctx.lineTo(100, 100);
  ctx.lineTo(200, 150);
  ctx.lineTo(10, 150);
  ctx.closePath();
  // Use the path just created as a clipping path
  ctx.clip();
  // Draw anywhere in the image, only inside the clipping path will be drawn
  ctx.drawImage(image,0,0, canvas.width, canvas.height);
  // restore so you can draw outside the clipping path again
  ctx.restore();

});

image.src = 'http://www.marinadivenezia.it/media/467/w-876/h-506/00-MAPPA-2015-GENERICA.jpeg';

My code on fiddle is here: http://jsfiddle.net/eGjak/2789/

Regards Andreas

Andreas Baran
  • 669
  • 1
  • 12
  • 27
  • Yes, you can detect the X-Y coordinates of your click event, and with that details you can know if the click is inside the path or outside. – Marcos Pérez Gude Jun 01 '16 at 09:41
  • If clip area is a polygon you may look at this [How can I determine whether a 2D Point is within a Polygon?](http://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon) – jcubic Jun 01 '16 at 09:51
  • @jcubic this is built-in through the method isPointInPath() (and isPointInStroke()) both even-odd as well as non-zero winding tests. –  Jun 01 '16 at 10:00

1 Answers1

2

Using plain paths

Certainly, but lets refactor the code a little for reuse. If you extract the clipping path to a separate function so we can reuse it for hit test:

function makePath() {
  ctx.beginPath();
  ctx.moveTo(10, 10);
  ctx.lineTo(100,50);
  ctx.lineTo(100, 100);
  ctx.lineTo(200, 150);
  ctx.lineTo(10, 150);
  ctx.closePath();
}

we can then use it to create the clip:

  //...
  ctx.save();
  makePath();   // here
  ctx.clip();
  ctx.drawImage(image,0,0, canvas.width, canvas.height);
  ctx.restore();
  //...

as well as for hit-testing, though we would invert the boolean result to trigger it for clicks outside the path. For hit-testing we can use isPointInPath() which will use the current path and by default the non-zero winding rule:

canvas.onclick = function(e) {
  var rect = this.getBoundingClientRect(),
      x =   e.clientX - rect.left,
      y = e.clientY - rect.top;
    
  makePath();  // and here
  if (!ctx.isPointInPath(x, y)) alert("Outside");
};

You can also store the path in a Path2D object instead of in a function as here, depending on how new browsers you'd like to support.

Modified fiddle

Using Path2D

Instead of the function we can use as mentioned a Path2D object which can be reused like this. These objects are handy to store several path data eliminating the need to reconstruct each path you want to test. The drawback is that they are not supported in all browsers at the moment.

var p = new Path2D();
p.moveTo(10, 10);
p.lineTo(100,50);
p.lineTo(100, 100);
p.lineTo(200, 150);
p.lineTo(10, 150);
p.closePath();

Then use it for clipping:

  ctx.save();
  ctx.clip(p);  // hand over path object
  ctx.drawImage(image,0,0, canvas.width, canvas.height);
  ctx.restore();

And hit-testing:

if (!ctx.isPointInPath(p, x, y)) alert("Outside");

Modified fiddle

Community
  • 1
  • 1
  • this works perfect thanks! Is there a way to help the users who have clicked wrong, so if they click outsite, I will force the mouse click to be in the clipped area, closes to the click they clicked outside? – Andreas Baran Jun 01 '16 at 11:12
  • @AndreasBaran no problem! you could break up the path using interpolation into point array, then measure distance to each to find the closest and then lock the coordinates to the each if mouse click is outside. One way could be like in [this answer](http://stackoverflow.com/questions/21563876/html5-canvas-drag-image-along-path-with-coordinates/21564847#21564847). –  Jun 01 '16 at 12:31