Regions
Depending on how well you want to support various and older browsers, there is addHitRegion()
that you can use by enabling it through flags in Firefox and Chrome (at the moment of this being written):
Firefox: about:config -> search "hitregions" and set to true
Chrome: chrome://flags/ -> Enable experimental canvas features
This is the only technique that integrates directly with the event system. I would not recommend it for production quite yet though, and AFAIK there is not a polyfill for it either - but to show how easy it is to use:
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>
Path: isPointInPath
The other techniques require one to manually implement a mechanism for hit-detection. One is by using isPointInPath()
. You simply rebuild the paths you want to test, one by one, then run your (adjusted) x/y mouse coordinate against it:
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>
Path: Path2D objects
For the latter example there is also the new Path2D objects which can hold a path on their own - the advantage here is that you don't need to rebuild the paths, just pass in the path object with x/y to the isPointInPath()
method.
The problem is that Path2D is not supported in all browsers yet, but there is this polyfill that will fix that for you,
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>
Manually check boundary
And of course, there is the old technique of using manual boundary checks. This will work in all browsers. Here the advisable thing to do is to create objects that holds the bounds and can also be used to render it. This typically limits you to rectangular areas - more complex shapes will require more complex algorithms (such as the isPointInPath()
embeds).
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>