How to Add a Simple Onclick Event Handler to a Canvas Element

How do I add a simple onClick event handler to a canvas element?

When you draw to a canvas element, you are simply drawing a bitmap in immediate mode.

The elements (shapes, lines, images) that are drawn have no representation besides the pixels they use and their colour.

Therefore, to get a click event on a canvas element (shape), you need to capture click events on the canvas HTML element and use some math to determine which element was clicked, provided you are storing the elements' width/height and x/y offset.

To add a click event to your canvas element, use...

canvas.addEventListener('click', function() { }, false);

To determine which element was clicked...

var elem = document.getElementById('myCanvas'),
elemLeft = elem.offsetLeft + elem.clientLeft,
elemTop = elem.offsetTop + elem.clientTop,
context = elem.getContext('2d'),
elements = [];

// Add event listener for `click` events.
elem.addEventListener('click', function(event) {
var x = event.pageX - elemLeft,
y = event.pageY - elemTop;

// Collision detection between clicked offset and element.
elements.forEach(function(element) {
if (y > element.top && y < element.top + element.height
&& x > element.left && x < element.left + element.width) {
alert('clicked an element');
}
});

}, false);

// Add element.
elements.push({
colour: '#05EFFF',
width: 150,
height: 100,
top: 20,
left: 15
});

// Render elements.
elements.forEach(function(element) {
context.fillStyle = element.colour;
context.fillRect(element.left, element.top, element.width, element.height);
});​

jsFiddle.

This code attaches a click event to the canvas element, and then pushes one shape (called an element in my code) to an elements array. You could add as many as you wish here.

The purpose of creating an array of objects is so we can query their properties later. After all the elements have been pushed onto the array, we loop through and render each one based on their properties.

When the click event is triggered, the code loops through the elements and determines if the click was over any of the elements in the elements array. If so, it fires an alert(), which could easily be modified to do something such as remove the array item, in which case you'd need a separate render function to update the canvas.


For completeness, why your attempts didn't work...

elem.onClick = alert("hello world"); // displays alert without clicking

This is assigning the return value of alert() to the onClick property of elem. It is immediately invoking the alert().

elem.onClick = alert('hello world');  // displays alert without clicking

In JavaScript, the ' and " are semantically identical, the lexer probably uses ['"] for quotes.

elem.onClick = "alert('hello world!')"; // does nothing, even with clicking

You are assigning a string to the onClick property of elem.

elem.onClick = function() { alert('hello world!'); }; // does nothing

JavaScript is case sensitive. The onclick property is the archaic method of attaching event handlers. It only allows one event to be attached with the property and the event can be lost when serialising the HTML.

elem.onClick = function() { alert("hello world!"); }; // does nothing

Again, ' === ".

How do I add a onClick event handler to a canvas element (arc)?

You need to redraw the shape before calling the method isPointInPath.

Ive added to your code a function to detect the mouse position and an other one that draws the element. I hope it helps.

var elements = [];var canvas = document.getElementById("myCanvas");canvas.width = "500";canvas.height = "500";var context = canvas.getContext("2d");
var mouse;
elements.push({ colour: "#112F41", x: 200, y: 240, r: 100, sAngle: 0, eAngle: 2 * Math.PI});
elements.forEach(element => { drawElement(element, context);});
canvas.addEventListener( "click", function(event) { mouse = oMousePos(canvas, event) elements.forEach(function(element) { drawElement(element, context); if(context.isPointInPath(mouse.x, mouse.y)){console.log(mouse)}else{console.log("not in path")} }); }, false);
function drawElement(element, context) { context.strokeStyle = element.colour; context.arc(element.x, element.y, element.r, element.sAngle, element.eAngle); context.lineWidth = 2;
context.stroke();}
function oMousePos(canvas, evt) { var ClientRect = canvas.getBoundingClientRect(); return { //objeto x: Math.round(evt.clientX - ClientRect.left), y: Math.round(evt.clientY - ClientRect.top) }}
canvas {  border:1px solid;}
<canvas id="myCanvas"></canvas>

How do I add a onClick event handler to a canvas element and control mouse movement actions?

First, you have to check if your mouse is on the image, and then check if you are trying to drag the image. To do that, you need some events, mousedown, mouseup and mousemove. To check if your mouse pointer is on the image, you have to get the X, Y, width, height of that image. Final code below.

Edit

Some more changes. Image class has no X and Y properties so I had to define variables that will store that data and make some changes to isInside function.

var canvas = document.createElement('canvas');document.body.appendChild(canvas);var context = canvas.getContext('2d');canvas.width = 300;canvas.height = 300;var upload_image;var imageX, imageY;var mouseX, mouseY;var imageDrag = false;
make_base();
canvas.addEventListener("mousemove", function (evt) { var mousePos = getMousePos(canvas, evt); mouseX = mousePos.x; mouseY = mousePos.y;});
function getMousePos(canvas, event) { var rect = canvas.getBoundingClientRect(); return { x: event.clientX - rect.left, y: event.clientY - rect.top };}
function isInsideImage(rect) { var pos = { x: mouseX, y: mouseY }; return pos.x > imageX && pos.x < imageX + rect.width && pos.y < imageY + rect.height && pos.y > imageY;}
function make_base(){ upload_image = new Image(); imageX = 0; imageY = 0; upload_image.onload = function(){ context.drawImage(upload_image, 0, 0); } upload_image.src = 'https://lh3.googleusercontent.com/-6Zw-hozuEUg/VRF7LlCjcLI/AAAAAAAAAKQ/A61C3bhuGDs/w126-h126-p/eagle.jpg';}
canvas.addEventListener("mousedown", function (evt) { if(isInsideImage(upload_image)) { imageDrag = true; }});
canvas.addEventListener("mouseup", function (evt) { if(imageDrag) imageDrag = false;});
setInterval(function() { if(imageDrag) { context.clearRect(0, 0, canvas.width, canvas.height); imageX = mouseX; imageY = mouseY; context.drawImage(upload_image, imageX, imageY); }}, 1000/30);

How to use multiple click event on a canvas

Letting the browser do (most of) the work

On click of the red part it should call function1 and on click of gray part, it should call function2.

You can use reusable path objects to store the different paths you want to test against and then use isPointInPath() with path and coordinates as arguments.

By checking each path for a hit you can assign a different call based on for example index.

And there is no need to do this:

var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

This will simply reference the same context as a canvas can only have one - if requested more than once the same will be given (or null if different type).

How to use multiple click event on a canvas

You can share the click handler to do what you want as shown below -

For modern browsers you can use both Path2D objects to store path information you want to use later (I'll address older browsers in the second example).

Example

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var p1 = new Path2D();
var p2 = new Path2D();
var paths = [p1, p2]; // store paths in array for check later

// store arc parts to respective path objects
p1.arc(100, 75, 50, -0.1 * Math.PI, 1.7 * Math.PI); // red part
p2.arc(100, 75, 50, 1.7 * Math.PI, -0.1 * Math.PI); // grey part

// render the two path objects using a common context, but different style
ctx.lineWidth = 15;

ctx.strokeStyle = "#c32020";
ctx.stroke(p1);

ctx.strokeStyle = "#a9a9a9";
ctx.stroke(p2);

// check for clicks on common canvas
c.onclick = function(e) {
var rect = this.getBoundingClientRect(), // adjust click coordinates
x = e.clientX - rect.left,
y = e.clientY - rect.top;

// iterate through path array to test each path for hits
for(var i = 0; i < paths.length; i++) {
if (ctx.isPointInStroke(paths[i], x, y)) { // check which path
console.log("Path " + (i+1) + " clicked");
break;
}
}
};
<canvas id="myCanvas"></canvas>

Onclick listener in canvas

Try this..

document.getElementById('canvasId').addEventListener('click',function(evt){
alert(evt.clientX + ',' + evt.clientY);
},false);

html5: how to handle canvas child clicked event?

The way to do this, is by handling the click event of the canvas, and then figure out yourself what part of the canvas is clicked:

canvas.addEventListener("mouseup", doMouseClick, false);

var doMouseClick = function(event)
{
//Some magic to have the exact coordinates in the canvas:
var clickPos = {
x : event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - Math.floor($(canvas).offset().left);,
y : event.clientY + document.body.scrollTop + document.documentElement.scrollTop - Math.floor($(canvas).offset().top) + 1
}

//check the image
if (clickPos.x > 128 && clickPos.y < 128 + imageObj.width
&& clickPos.y > 128 && clickPos.y < 128 + imageObj.height){
console.log("Oh boy! Someone clicked the image!");
}

}

The reason why we have to do it this way, is because the you don't "add" your image as a child to the canvas, you "draw" it on the canvas. The canvas just shows pixels and has no idea what is actually drawn.

How to add an onClick event handler to a canvas shape in Elm?

In Elm, using the Canvas rendering, you should use the Mouse.clicks signal and react to changes in the signal. Here's a runnable example of how that would work:

import Graphics.Element exposing (Element, show)
import Mouse

clicks : Signal (Int, Int)
clicks =
Signal.sampleOn Mouse.clicks Mouse.position

main : Signal Element
main =
Signal.map show clicks

In essence, Mouse.clicks are the actual "events" we are interested in, so whenever one happens, we "sample" the Mouse.position signal to get the click position.

Signal.sampleOn produces a signal that updates with the value of the second parameter signal (here, the mouse position) whenever there is a change in the first parameter signal (here, the mouse clicks).

Now, just to get the result showing, we are also mapping the position to the show function in main.

You can also paste this code to http://elm-lang.org/try, compile and try clicking the right-hand side to see it working.



Related Topics



Leave a reply



Submit