May be some one has had similar experiences on this dilemma and can help me out here...
Basically, I have a canvas element on which I draw several rectangles in a loop using
context.fillRect (x, y, width, height)
Now, I want some of the rectangles to be hotspots and respond to click events. I can find out the exact (x,y) of a click event using event.layerX
and event.layerY
.
Given that I know the following:
how do I find out if the click event occurred inside the perimeter a certain rectangle or not?
and,
which rectangle the click event occurred 0n?
Is there like a mathematical formula for this?
Any help would be much appreciated, and if I'm not clear enough, let me know...
Thanks
EDIT
Is there no better way than to loop through all rectangles and check their position and dimensions?
This is a general topology problem frequently encountered when using maps.
The algorithm for deciding whether a point is within ANY polygon (rectangles, circles, irregular shapes) is as follows:
From the point being checked construct any line in any direction until the edge of the screen area in which your polygons are located
if the line intersects any polygon boundary at an odd number of places then it is within that polygon
if the line intersects any polygon boundary an even number of places it is outside that polygon.
------------
| |
| o--|------------------- 1 intersection: inside
| |
-------------
------------
| |
o------|-----------|------------------- 2 intersections: outside
| |
-------------
Notes:
The direction of the line is irrelevant
Will not work if a polygon is cut at the side of the screen without closure
If the polygon cuts over itself then the algorithm can fail if the line happens to pass through the cutpoint (eg in the figure 8 considered as a single polygon where the line passes exactly through where upper and lower parts connect of the figure connect)
I hate to post a ton of code here, but here's a js class that might help you...
function Point(x,y){
this.x=x;
this.y=y;
}
function Polygon(){
this.points=[];
this.x_min=undefined;
this.x_max=undefined;
this.y_min=undefined;
this.y_max=undefined;
this.add = function(p){
this.points=this.points.concat(p);
if (p.x<this.x_min){this.x_min=p.x;}
if (p.x>this.x_max){this.x_max=p.x;}
if (p.y<this.y_min){this.y_min=p.y;}
if (p.y>this.y_min){this.y_max=p.y;}
}
this.pointInPoly = function(p){
var j=(this.points.length-1); //start by testing the link from the last point to the first point
var isOdd=false;
//check the bounding box conditions
if (p.x < this.x_min || p.x > this.x_max || p.y < this.y_min || p.y > this.y_max){
return false;
}
//if necessary use the line crossing algorithm
for(var i=0; i<this.points.length; i++){
if ((this.points[i].y<p.y && this.points[j].y>=p.y) ||
(this.points[j].y<p.y && this.points[i].y>=p.y)) {
if (this.points[i].x+(p.y-this.points[i].y)/(this.points[j].y-
this.points[i].y)*(this.points[j].x-this.points[i].x)<p.x)
{ isOdd=(!isOdd);} }
j=i;
}
return isOdd;
}
}
PS: you'll need to convert your click event to local coordinates using something like the following function (where e is your click event):
function getPosition(e){
var p = new Point();
if (e.pageX != undefined && e.pageY != undefined) {
p.x = e.pageX;
p.y = e.pageY;
}
else {
p.x = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
p.y = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
p.x -= gCanvas.offsetLeft;
p.y -= gCanvas.offsetTop;
return p;
}
Roughly, you can do it like this:
var click_x = event.layerX;
var click_y = event.layerY;
for ( var i = 0; i < rects.length; i++ ) {
var rect = rects[i];
if ( click_x >= rect.x && click_x <= rect.x + rect.width
&& click_y >= rect.y && click_y <= rect.y + rect.height ) {
// The click was inside the rectange
}
}
Assuming we're dealing with non-negative widths and heights :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With