javascript 将 onclick 和 onmouseover 添加到画布元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16628184/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Add onclick and onmouseover to canvas element
提问by Mr SVG
I want to add an onclick
, onmouseover
and an onmouseout
events to individual shapes in a canvas element.
我想向画布元素中的各个形状添加一个onclick
,onmouseover
和一个onmouseout
事件。
I have tried doing this with SVG in different ways and found no single method will work in all the major browsers.
我曾尝试以不同的方式使用 SVG 执行此操作,但发现没有一种方法适用于所有主要浏览器。
Maybe, is there a simple way to add an onclick
and probably other events to canvas shapes?
也许,有没有一种简单的方法可以onclick
向画布形状添加一个和可能的其他事件?
Can someone please show me how to add an onclick
?
有人可以告诉我如何添加onclick
吗?
Here is my code:
这是我的代码:
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")
}
回答by markE
Here is a barebones framework for adding events to individual canvas shapes
这是将事件添加到单个画布形状的准系统框架
Here's a preview: http://jsfiddle.net/m1erickson/sAFku/
这是一个预览:http: //jsfiddle.net/m1erickson/sAFku/
Unlike SVG, after you draw a shape to canvas, there is no way to identify that shape.
与 SVG 不同,在画布上绘制形状后,无法识别该形状。
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.
- x 位置,
- y 位置,
- 宽度,
- 高度。
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.
然后我们可以使用“cookie-cutter”类来创建该形状的多个副本。
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.
这是我们的 rect 类,它“记住”关于任何自定义矩形的所有基本信息。
// 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".
当我们使用“cookie-cutter”类创建 1+ 个实际矩形以在画布上绘制时,生成的实际矩形称为“对象”。
Here we create 3 real rectangle objects from our 1 class. We have assigned each real object different width, height and colors.
在这里,我们从 1 个类创建了 3 个真正的矩形对象。我们为每个真实物体分配了不同的宽度、高度和颜色。
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.
现在让我们通过添加 draw() 函数让我们的类能够在画布上绘制自己。这是我们放置画布上下文绘制命令和样式命令的地方。
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.
下面介绍如何使用 draw() 函数在画布上绘制矩形。请注意,我们有 2 个矩形对象,我们必须对它们执行 .draw() 才能在画布上显示 2 个矩形。
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.
现在让 rect 类能够让我们知道某个点(鼠标)是否在该 rect 内。当用户生成鼠标事件时,我们将使用这个 isPointInside() 函数来测试鼠标当前是否在我们的矩形内。
// 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.
最后,我们可以将我们的 rect 类绑定到普通的浏览器鼠标事件系统中。
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.
我们要求 jQuery 监听画布上的鼠标点击。然后我们将该鼠标位置提供给 rect 对象。我们使用矩形的 isPointInside() 来报告点击是否在矩形内。
// 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:
对于一个圆,isPointInside() 看起来像这样:
// 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!
即使形状不规则的画布元素也可以有 isPointInside,但这通常会变得复杂!
That's it!
而已!
Here is slightly enhanced code and a Fiddle: http://jsfiddle.net/m1erickson/sAFku/
这是稍微增强的代码和小提琴: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>
回答by Mr SVG
Added a more up-to-date answer: since this question was posted there are now two new techniques that can be used to detect local clicks in a canvas element:
添加了最新的答案:由于发布了这个问题,现在有两种新技术可用于检测画布元素中的本地点击:
Path2D
: Paths can be stored on separate Path2D objects and checked usingisPointInPath()
addHitRegion
: integrates with the event system which will allow you to check the event object itself of regions
Path2D
:路径可以存储在单独的 Path2D 对象上并使用isPointInPath()
addHitRegion
: 与事件系统集成,这将允许您检查区域的事件对象本身
Path2D example
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)) { ... }
Read more about Path2D here. A polyfillexists as well.
在此处阅读有关 Path2D 的更多信息。一个polyfill 也存在。
addHitRegion example
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)
}
});
Read more about addHitRegion()
here.
addHitRegion()
在这里阅读更多信息。
Note that it is still a bit early, both Firefox and Chrome supports this enabled via flags, other browsers will hopefully follow suit.
请注意,现在还为时过早,Firefox 和 Chrome 都支持通过标志启用此功能,其他浏览器有望效仿。
回答by Hugo Tunius
In short you cannot add listeners to shapes in a canvas because the shapes aren't exposed as objects. The most straightforward way to implement this is to use a a single listener on the canvas and loop through all the objects drawn in the canvas to find the correct one.
简而言之,您不能向画布中的形状添加侦听器,因为这些形状不会作为对象公开。实现这一点的最直接方法是在画布上使用单个侦听器并循环遍历画布中绘制的所有对象以找到正确的对象。
Thisanswer explains how to implement this using the library Raphael which also gives you a lot of other benefits.
这个答案解释了如何使用 Raphael 库来实现这一点,这也给你带来了很多其他好处。
If you don't want to use a library this is a very short example of doing it.
如果您不想使用库,这是一个非常简短的示例。
rects = [{ color : "blue", origin : { x : 10, y : 10 }, size : { width : 60, height: 60}},
{ color : "red", origin : { x : 80, y : 60 }, size : { width : 60, height: 60}}]
function onClick(event) {
var length = rects.length;
for (var i = 0; i < length; ++i) {
var obj = rects[i];
if (event.x > obj.x && event.x < obj.origin.x + obj.size.width &&
event.y > obj.y && event.y < obj.origin.y + obj.size.height) {
console.log("Object clicked: " + obj);
}
}
NOTE:If you have a lot of objects this approach could be a bit slow. This can be combated by using a 2D spatial data structure.
注意:如果您有很多对象,这种方法可能会有点慢。这可以通过使用二维空间数据结构来解决。
回答by Stefan Haustein
The main difference between Canvas and SVG is that Canvas does not retain information about shapes drawn other than resulting changes in the pixel array.
Canvas 和 SVG 之间的主要区别在于 Canvas 不保留有关绘制的形状的信息,除了像素阵列的结果变化。
So one option would be to recognize the shapes by the corresponding pixel color value in the mouse click handler:
因此,一种选择是通过鼠标单击处理程序中相应的像素颜色值来识别形状:
function onClick(event) {
var data = ctx.getImageData(event.x, event.y, 1, 1);
var red = data[0];
var green = data[1];
var blue = data[2];
var color = red << 16 | green << 8 | blue;
if (color == 0x0000ff) {
blue();
} else if (color == 0x0ff0000) {
red();
}
}
If you want to track clicks for multiple objects with the same color using this approach, you'll need to slightly change the color for each shape to make it trackable.
如果要使用此方法跟踪多个具有相同颜色的对象的点击次数,则需要稍微更改每个形状的颜色以使其可跟踪。
This approach won't work when you add images from a different host because in this case the same origin policy will prevent getImageData.
当您添加来自不同主机的图像时,此方法将不起作用,因为在这种情况下,同源策略将阻止 getImageData。