的onclick和的onmouseover添加到画布元素(Add onclick and onmou

2019-09-16 04:03发布

我要添加onclickonmouseoveronmouseout事件单个形状的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")
}

Answer 1:

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>


Answer 2:

总之,你不能在画布上添加侦听器的形状,因为形状不公开为对象。 实现这个最简单的方法是使用画布和环路上AA单监听器通过在画布上绘制的所有对象,找到正确的一个。

这个答案说明了如何实现此使用库拉斐尔这也给了你很多其他的好处。

如果你不想使用图书馆,这是做的一个很短的例子。

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);
        }
    }

注意:如果你有大量的对象这种做法可能会有点慢。 这可以通过使用一个2D空间数据结构予以打击。



Answer 3:

增加了一个更先进的最新答案 :因为这个问题被张贴现在是可以用来检测一个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支持通过标志启用此功能后,其他浏览器将有望步其后尘。



Answer 4:

画布和SVG之间的主要区别是,帆布不保留关于比得到的像素阵列中的变化绘制的其它形状的信息。

因此,一个办法是通过在鼠标单击处理对应的像素颜色值来识别形状:

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();
  }
}

如果你想用这种方法来跟踪同色多个对象的点击,你需要稍微改变颜色,每一种形状,使其可跟踪。

当你从一个不同的主机添加图像,因为在这种情况下,同源策略会阻止getImageData这种做法是行不通的。



文章来源: Add onclick and onmouseover to canvas element