If you need to interact with a specific object in the canvas you have to face a problem.
Using DOM you can click on a node and the browser manages the whole interaction (triggers the event, puts the DOM node in event.target, etc.).
But, as explained before, once you have drawn an object to a canvas it become a part of the canvas. So the question is: how to detect when a user click on a specific shape?
Getting canvas related coordinates
The first obstacle to get through is getting where a user clicked on the canvas. When a user clicks you can get the mouse coordinates relative to the page and you must subtract the canvas position:
canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top;
...
It's not too complicate, isn't it?
Checking in reverse order
var objs = [
{name:'shape2',
color:'green',
x:30,
y:50,
angle:Math.PI/2,
width: 40,
height: 50
},
{name:'shape1',
color:'red',
x:40,
y:30,
angle:Math.PI/4,
width: 30,
height: 40
}
];
Then each shape is drawn in order over the previous.
For this reason I have to check shapes in reverse order, from the object in foreground to the one in background.
for (i = objs.length - 1; i >= 0; i--){
obj = objs[i];
...
Pointer transformations
Now I have pointer coordinates and shape transformations:
How to detect if pointer coordinates are inside this shape?
My solution is a bit imaginative but it seems to work well.
{name:'shape1',
color:'red',
x:40, // translation x
y:30, // translation y
angle:Math.PI/4, // rotation
width: 30,
height: 40
}
How to detect if pointer coordinates are inside this shape?
My solution is a bit imaginative but it seems to work well.
I apply transformations to pointer coordinates. These are the opposite that I have applied to the object before drawing it to the canvas. Then I check if the new pointer coordinates are inside the object shape pretending that the shape has drawn around 0, 0.
// pointer transformation
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
if (p.x > -(obj.width / 2) && p.x < obj.width / 2 && p.y > - (obj.height / 2) && p.y < obj.height / 2){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
I wrote this simple object to help with transformations (see part 1 for an explanation on transformations):
var Point = function (x, y){
this.x = x;
this.y = y;
};
Point.prototype.rotate = function (angle){
var x, y;
x = this.x*Math.cos(angle) - this.y * Math.sin(angle);
y = this.x*Math.sin(angle) + this.y * Math.cos(angle);
this.x = x;
this.y = y;
return this;
};
Point.prototype.translate = function (x, y){
this.x = this.x + x;
this.y = this.y + y;
return this;
};
This is the whole code:
canvas.addEventListener('click', function (evt){
var rect = canvas.getBoundingClientRect(),
posx = evt.clientX - rect.left,
posy = evt.clientY - rect.top,
i, obj,p;
// I get the position of the pointer
// relative to the canvas
for (i = objs.length - 1; i >= 0; i--){ // cycling in inverse order
obj = objs[i];
// pointer transformation
p = new Point(posx, posy);
p.translate(-obj.x, -obj.y);
p.rotate(-obj.angle);
if (p.x > -(obj.width / 2) && p.x < obj.width / 2 && p.y > - (obj.height / 2) && p.y < obj.height / 2){
log.innerHTML = 'clicked on ' + obj.name;
return;
}
}
log.innerHTML = '';
}, false);
This is the result (try to click on a shape):