Add onclick and onmouseover to the canvas element

I want to add onclick , onmouseover and onmouseout to individual shapes in the canvas element.

I tried to do this using SVG in different ways and did not find that no method would work in all major browsers.

Maybe there is an easy way to add onclick and possibly other events to canvas forms?

Can someone please show me how to add 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") } 
+8
javascript canvas onclick onmouseover
source share
4 answers

Here is the best seller to add events to individual canvas shapes.

Here's a preview: http://jsfiddle.net/m1erickson/sAFku/

Unlike SVG, after you draw a shape on the canvas, there is no way to identify that shape.

There are no separate figures on the canvas, there is only a canvas with pixels.

To be able to identify and "use" any individual canvas form, you need to remember all the basic properties of this form.

Here are the properties needed to identify the rectangle:

  • x-position
  • y-position
  • width
  • height.

You will also want to remember some basic properties of a rectangle style:

  • fillcolor,
  • strokecolor,
  • strokewidth.

So, here's how to create a rectangular class object that remembers all its basic properties.

If you are not familiar with the term “class”, think of it as a “cookie cutter,” which we can use to define the form.

Then we can use the cookie-cutter class to create multiple copies of this shape.

Even better ... the classes are flexible enough to allow us to change the basic properties of each copy we made.

For rectangles, we can use our one class to create many, many rectangles of different widths, heights, colors, and locations.

The key point here is that we create classes because classes are very flexible and reusable!

Here is our rectangular class that “remembers” all the basic information 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 diversity needs.

When you create the actual rectangle (filling it with properties), each copy of our cutter cube has its own set of properties.

When we use the cookie-cutter class to create 1 + actual rectangles for drawing on canvas, the resulting real rectangles are called “objects”.

Here we create 3 real rectangular objects from our 1 class. We assigned each real object a different width, height and color.

 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 opportunity to draw themselves on the canvas by adding the draw () function. Here we put 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. Please note that we have 2 rectangle objects, and we must execute .draw () for both of them for 2 rectangles that will be displayed 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 opportunity to tell us if the point (mouse) is inside this rectangle. When the user generates mouse events, we will use this isPointInside () function to check if the mouse is inside our rectangle.

 // 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 associate our rectangular class with a regular mouse event system.

We ask jQuery to listen for mouse clicks on the canvas. Then we root this mouse position on the direct object. We use rect isPointInside () to report if the click was inside the rectangle.

 // listen for click events and trigger handleMouseDown $("#canvas").click(handleMouseDown); // calc the mouseclick position and test if it 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 ... how do you "remember" the shape of the canvas and how do you execute the code in your question!

 alert("hello from blue square") 

This is a basketball “class” that creates various rectangles and mouseclicks posts.

You can use this template as a starting point for listening to all mouse events on all kinds of canvas shapes.

Almost all forms of canvas are rectangular or round.

Rectangular Canvas Elements

  • Rectangle
  • Picture
  • Text
  • Line (yes!)

Circular Canvas Elements

  • a circle
  • Arc
  • Normal polygon (yes!)

Irregular canvas elements

  • Curves (cubic and square bezier)
  • Way

. isPointInside () will 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 this usually gets complicated!

Here it is!

Here is some improved code and script: 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> 
+17
source share

In short, you cannot add listeners to shapes in the canvas, because shapes are not displayed as objects. The easiest way to implement this is to use a single listener on the canvas and skip all the objects drawn on the canvas to find the correct one.

This answer explains how to implement this using the Raphael library, which also gives you many other benefits.

If you do not want to use the library, this is a very short example of this.

 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 can be a little slow. You can deal with this using the 2D spatial data structure.

+1
source share

Added more modern answer . Since this question was posted, there are now two new methods that can be used to detect local clicks in the canvas element:

  • Path2D : Paths can be stored on separate Path2D objects and checked using isPointInPath()
  • addHitRegion : integrates with the event system, which allows you to check the regions event object itself

Path2D Example

  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)) { ... } 

More about Path2D here . A polyfill exists.

AddHitRegion example

 // 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 .

Note that it's still a bit early, and Firefox and Chrome support this with flags, other browsers hopefully follow suit.

+1
source share

The main difference between Canvas and SVG is that Canvas does not store information about shapes drawn differently than the resulting changes in the pixel array.

Thus, one of the options would be pattern recognition using 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 need to slightly change the color for each shape to make it trackable.

This approach will not work if you add images from another host, because in this case the same origin policy will prevent getImageData.

0
source share

All Articles