HTML5 canvas: find out if click co-ordinates are inside a given rectangle
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:
- the exact x,y of the click
- the x,y,width and height of every rectangle
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 t开发者_高级运维here 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 :)
This may be overkill, but Cake JS will do the job for rectangles and other shapes. Check out the demo.
I have recently created a API for detecting mouse clicks on rectangles. It's very basic but but it should solve your problem
You can find it here.
Note: It currently doesn't have a text support for the buttons but it does have support for some basic styles
Documentation:
Here
Code:
function canvasButton(x, y, w, h, style , text) {
this.style = style
this.x = x || 0;
this.y = y || 0;
this.w = w || 1;
this.h = h || 1;
this.fill = style.default.fill ;
this.text = text || undefined;
this.onclick = function() {alert("Click! Make sure to add the onclick listener to your button!")}
this.mouseup = function() {alert("Mouseup! Make sure to add the mouseup listener to your button!")}
this.addListener = function() {
var buttonX = this.x;
var buttonY = this.y;
var buttonWidth = this.w;
var buttonHeight = this.h;
var onclick = this.onclick
var mouseup = this.mouseup
var styles = this.style
var draw = this.draw
var ctx = this.ctx
this.ctx.canvas.addEventListener('mousedown',function(e){
var click_x = e.clientX;
var click_y = e.clientY;
if ( click_x >= buttonY && click_x <= buttonX + buttonWidth && click_y >= buttonY && click_y <= buttonY + buttonHeight ) {
onclick()
ctx.fillStyle = style.active.fill
ctx.fillRect(buttonX, buttonY, buttonWidth, buttonHeight);
}
})
this.ctx.canvas.addEventListener('mouseup',function(e) {
var click_x = e.clientX;
var click_y = e.clientY;
if ( click_x >= buttonY && click_x <= buttonX + buttonWidth && click_y >= buttonY && click_y <= buttonY + buttonHeight ) {
mouseup()
ctx.fillStyle = style.default.fill
ctx.fillRect(buttonX, buttonY, buttonWidth, buttonHeight);
}
})
}
}
// Draws this shape to a given context
canvasButton.prototype.draw = function(ctx) {
this.ctx = ctx
ctx.fillStyle = this.fill;
ctx.font = this.font
ctx.fillRect(this.x, this.y, this.w, this.h);
ctx.fillText(this.text,this.x,this.y)
this.addListener();
}
//Use it like this:
var start = new canvasButton(x,y,height,width,{ // Styles
"default": {
"fill":"#765",
},
"active": {
"fill":"#876",
}
})
//Event Listeners
start.mousedown = function() {
}
start.mouseup = function() {
}
start.draw(document.getElementById("canvas").getContext("2d")); // Draw to canvas
精彩评论