我要添加onclick
, onmouseover
和onmouseout
事件单个形状的canvas元素。
我曾尝试以不同的方式与SVG这样做,并没有发现任何单一的方法将在所有主要的浏览器。
也许,有一个简单的方法来添加onclick
,可能其他事件帆布形状?
可有人请告诉我如何添加onclick
?
这里是我的代码:
canvas
{
background:gainsboro;
border:10px ridge green;
}
<canvas id="Canvas1"></canvas>
var c=document.getElementById("Canvas1");
var ctx=c.getContext("2d");
ctx.fillStyle="blue";
ctx.fillRect(10,10,60,60);
var ctx=c.getContext("2d");
ctx.fillStyle="red";
ctx.fillRect(80,60,60,60);
// these need an onclick to fire them up. How do I add the onclick
function blue()
{
alert("hello from blue square")
}
function red()
{
alert("hello from red square")
}
Here is a barebones framework for adding events to individual canvas shapes
Here's a preview: http://jsfiddle.net/m1erickson/sAFku/
Unlike SVG, after you draw a shape to canvas, there is no way to identify that shape.
On canvas, there are no individual shapes, there is just a canvas full of pixels.
To be able to identity and “use” any individual canvas shape, you need to remember all basic properties of that shape.
Here are the properties necessary to identify a rectangle:
- x-position,
- y-position,
- width,
- height.
You will also want to remember some basic styling properties of a rectangle:
- fillcolor,
- strokecolor,
- strokewidth.
So here is how to create a rectangle “class” object that remembers all of it’s own basic properties.
If you're not familiar with the term "class", think of it as a "cookie-cutter" that we can use to define a shape.
Then we can use the "cookie-cutter" class to create multiple copies of that shape.
Even better ... classes are flexible enough to let us modify the basic properties of each copy that we make.
For rectangles, we can use our one class to make many, many rectangles of different widths, heights, colors and locations.
The key here is that we create classes because classes are Very Flexible and Reusable!
Here is our rect class that "remembers" all the basic info about any custom rectangle.
// the rect class
function rect(id,x,y,width,height,fill,stroke,strokewidth) {
this.x=x;
this.y=y;
this.id=id;
this.width=width;
this.height=height;
this.fill=fill||"gray";
this.stroke=stroke||"skyblue";
this.strokewidth=strokewidth||2;
}
We can reuse this class to create as many new rectangles as we need...And we can assign different properties to our new rectangles to meet our needs for variety.
When you create an actual rectangle (by filling in it's properties), every "cookie-cutter" copy of our class has its own private set of properties.
When we use a "cookie-cutter" class to create 1+ actual rectangles to draw on the canvas, the resulting real rectangles are called "objects".
Here we create 3 real rectangle objects from our 1 class. We have assigned each real object different width, height and colors.
var myRedRect = new rect("Red-Rectangle",15,35,65,60,"red","black",3);
var myGreenRect = new rect("Green-Rectangle",115,55,50,50,"green","black",3);
var myBlueRect = new rect("Blue-Rectangle",215,95,25,20,"blue","black",3);
Now let’s give our class the ability to draw itself on the canvas by adding a draw() function. This is where we put the canvas context drawing commands and styling commands.
rect.prototype.draw(){
ctx.save();
ctx.beginPath();
ctx.fillStyle=this.fill;
ctx.strokeStyle=this.stroke;
ctx.lineWidth=this.strokewidth;
ctx.rect(x,y,this.width,this.height);
ctx.stroke();
ctx.fill();
ctx.restore();
}
Here’s how to use the draw() function to draw rectangles on the canvas. Notice that we have 2 rectangle objects and we must execute .draw() on both of them for 2 rects to show on the canvas.
var myRedRect = new rect("Red-Rectangle",15,35,65,60,"red","black",3);
myRedRect.draw();
var myBlueRect = new rect("Blue-Rectangle",125,85,100,100,"blue","orange",3);
myBlueRect.draw();
Now give the rect class the ability to let us know if a point (mouse) is inside that rect. When the user generates mouse events, we will use this isPointInside() function to test if the mouse is currently inside our rect.
// accept a point (mouseposition) and report if it’s inside the rect
rect.prototype.isPointInside = function(x,y){
return( x>=this.x
&& x<=this.x+this.width
&& y>=this.y
&& y<=this.y+this.height);
}
Finally we can tie our rect class into the normal browser mouse event system.
We ask jQuery to listen for mouse clicks on the canvas. Then we feed that mouse position to the rect object. We use the rect's isPointInside() to report back if the click was inside the rect.
// listen for click events and trigger handleMouseDown
$("#canvas").click(handleMouseDown);
// calc the mouseclick position and test if it's inside the rect
function handleMouseDown(e){
// calculate the mouse click position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// test myRedRect to see if the click was inside
if(myRedRect.isPointInside(mouseX,mouseY)){
// we (finally!) get to execute your code!
alert("Hello from the "+myRedRect.id);
}
}
// These are the canvas offsets used in handleMouseDown (or any mouseevent handler)
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
Well...that's how you "remember" canvas shapes & how you execute the code in your question!
alert("hello from blue square")
That’s a barebones “class” that creates various rectangles and reports mouseclicks.
You can use this template as a starting point to listen for all mouse-events on all kinds of canvas shapes.
Almost all canvas shapes are either rectangular or circular.
Rectangular Canvas Elements
- Rectangle
- Image
- Text
- Line (yes!)
Circular Canvas Elements
- Circle
- Arc
- Regular Polygon (yes!)
Irregular Canvas Elements
- Curves (Cubic & Quad Beziers)
- Path
The isPointInside() would look like this for a circle:
// check for point inside a circlular shape
circle.prototype.isPointInside = function(x,y){
var dx = circleCenterX-x;
var dy = circleCenterY-y;
return( dx*dx+dy*dy <= circleRadius*circleRadius );
}
Even irregularly shaped canvas elements can have isPointInside, but that usually gets complicated!
That’s it!
Here is slightly enhanced code and a Fiddle: http://jsfiddle.net/m1erickson/sAFku/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
//
var rect = (function () {
// constructor
function rect(id,x,y,width,height,fill,stroke,strokewidth) {
this.x=x;
this.y=y;
this.id=id;
this.width=width;
this.height=height;
this.fill=fill||"gray";
this.stroke=stroke||"skyblue";
this.strokewidth=strokewidth||2;
this.redraw(this.x,this.y);
return(this);
}
//
rect.prototype.redraw = function(x,y){
this.x=x;
this.y=y;
ctx.save();
ctx.beginPath();
ctx.fillStyle=this.fill;
ctx.strokeStyle=this.stroke;
ctx.lineWidth=this.strokewidth;
ctx.rect(x,y,this.width,this.height);
ctx.stroke();
ctx.fill();
ctx.restore();
return(this);
}
//
rect.prototype.isPointInside = function(x,y){
return( x>=this.x
&& x<=this.x+this.width
&& y>=this.y
&& y<=this.y+this.height);
}
return rect;
})();
//
function handleMouseDown(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
var clicked="";
for(var i=0;i<rects.length;i++){
if(rects[i].isPointInside(mouseX,mouseY)){
clicked+=rects[i].id+" "
}
}
if(clicked.length>0){ alert("Clicked rectangles: "+clicked); }
}
//
var rects=[];
//
rects.push(new rect("Red-Rectangle",15,35,65,60,"red","black",3));
rects.push(new rect("Green-Rectangle",60,80,70,50,"green","black",6));
rects.push(new rect("Blue-Rectangle",125,25,10,10,"blue","black",3));
//
$("#canvas").click(handleMouseDown);
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
增加了一个更先进的最新答案 :因为这个问题被张贴现在是可以用来检测一个canvas元素当地点击两种新技术:
-
Path2D
:路径可以被存储在单独的Path2D对象和使用被查isPointInPath()
-
addHitRegion
:集成了事件系统,这将允许你检查事件对象本身的区域
Path2D例子
var path1 = new Path2D();
path1.rect(x1, y1, w, h); // add sub-path to Path2D object
var path2 = new Path2D();
path2.rect(x2, y2, w, h); // add sub-path to Path2D object
// now we can iterate through the objects to check which path we
// clicked without the need to rebuild each path as we did in the past
if (ctx.isPointInPath(path1, x, y)) { ... }
了解更多关于Path2D 这里 。 一个填充工具同样存在。
addHitRegion例子
// define a region using path
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.addHitRegion({id: "test"});
// now we can check in the event if the region was hit by doing:
canvas.addEventListener("mousemove", function(event){
if(event.region) {
// a region was hit, id can be used (see specs for options)
}
});
了解更多关于addHitRegion()
在这里 。
请注意,它仍然是一个有点早,Firefox和Chrome支持通过标志启用此功能后,其他浏览器将有望步其后尘。