How to Get the Coordinates of a Mouse Click on a Canvas Element

How do I get the coordinates of a mouse click on a canvas element?

If you like simplicity but still want cross-browser functionality I found this solution worked best for me. This is a simplification of @Aldekein´s solution but without jQuery.

function getCursorPosition(canvas, event) {
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
console.log("x: " + x + " y: " + y)
}

const canvas = document.querySelector('canvas')
canvas.addEventListener('mousedown', function(e) {
getCursorPosition(canvas, e)
})

Get the mouse coordinates when clicking on canvas

You could achieve that by using the offsetX and offsetY property of MouseEvent

//setup canvas

var canvasSetup = document.getElementById("puppyCanvas");

var ctx = canvasSetup.getContext("2d");

guessX = 0; //stores user's click on canvas

guessY = 0; //stores user's click on canvas

function storeGuess(event) {

var x = event.offsetX;

var y = event.offsetY;

guessX = x;

guessY = y;

console.log("x coords: " + guessX + ", y coords: " + guessY);

}
<canvas id="puppyCanvas" width="500" height="500" style="border:2px solid black" onclick="storeGuess(event)"></canvas>

Simulating a mouse click at (x, y) on an HTML5 canvas element

The problem with your jQuery approach is that you are mixing it with a plain JavaScript event listener. This does not work with jQuery.

If you want to trigger an event and listen it using jQuery you have to use it's .trigger() and .mousedown() methods.

Here's an example:

function getMousePosition(canvas, event) {
let rect = canvas.getBoundingClientRect();
let x = event.clientX - rect.left;
let y = event.clientY - rect.top;
document.getElementById('output').innerText = x + ", " + y;
}

canvasElem = document.querySelector("canvas");

$('#canvas_element').mousedown(function(e) {
getMousePosition(canvasElem, e);
});

var e = jQuery.Event("mousedown", {
clientX: 50,
clientY: 50
});
$('#canvas_element').trigger(e);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas_element"></canvas>
<div id="output"></div>

Getting mouse location in canvas

Easiest way is probably to add a onmousemove event listener to the canvas element, and then you can get the coordinates relative to the canvas from the event itself.

This is trivial to accomplish if you only need to support specific browsers, but there are differences between f.ex. Opera and Firefox.

Something like this should work for those two:

function mouseMove(e)
{
var mouseX, mouseY;

if(e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
}
else if(e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}

/* do something with mouseX/mouseY */
}

Real mouse position in canvas

The Simple 1:1 Scenario

For situations where the canvas element is 1:1 compared to the bitmap size, you can get the mouse positions by using this snippet:

function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}

Just call it from your event with the event and canvas as arguments. It returns an object with x and y for the mouse positions.

As the mouse position you are getting is relative to the client window you’ll have to subtract the position of the canvas element to convert it relative to the element itself.

Example of integration in your code:

// put this outside the event loop..
var canvas = document.getElementById("imgCanvas");
var context = canvas.getContext("2d");

function draw(evt) {
var pos = getMousePos(canvas, evt);

context.fillStyle = "#000000";
context.fillRect (pos.x, pos.y, 4, 4);
}

Note: borders and padding will affect position if applied directly to the canvas element so these needs to be considered via getComputedStyle() – or apply those styles to a parent div instead.

When Element and Bitmap are of different sizes

When there is the situation of having the element at a different size than the bitmap itself, for example, the element is scaled using CSS or there is pixel-aspect ratio etc. you will have to address this.

Example:

function  getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleX = canvas.width / rect.width, // relationship bitmap vs. element for x
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for y

return {
x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have
y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
}
}

With transformations applied to context (scale, rotation etc.)

Then there is the more complicated case where you have applied transformation to the context such as rotation, skew/shear, scale, translate etc. To deal with this you can calculate the inverse matrix of the current matrix.

Newer browsers let you read the current matrix via the currentTransform property and Firefox (current alpha) even provide an inverted matrix through the mozCurrentTransformInverted. Firefox however, via mozCurrentTransform, will return an Array and not DOMMatrix as it should. Neither Chrome, when enabled via experimental flags, will return a DOMMatrix but a SVGMatrix.

In most cases however you will have to implement a custom matrix solution of your own (such as my own solution here – free/MIT project) until this get full support.

When you eventually have obtained the matrix regardless of path you take to obtain one, you’ll need to invert it and apply it to your mouse coordinates. The coordinates are then passed to the canvas which will use its matrix to convert it to back wherever it is at the moment.

This way the point will be in the correct position relative to the mouse. Also here you need to adjust the coordinates (before applying the inverse matrix to them) to be relative to the element.

An example just showing the matrix steps:

function draw(evt) {
var pos = getMousePos(canvas, evt); // get adjusted coordinates as above
var imatrix = matrix.inverse(); // get inverted matrix somehow
pos = imatrix.applyToPoint(pos.x, pos.y); // apply to adjusted coordinate

context.fillStyle = "#000000";
context.fillRect(pos.x-1, pos.y-1, 2, 2);
}

An example of using currentTransform when implemented would be:

  var pos = getMousePos(canvas, e);          // get adjusted coordinates as above
var matrix = ctx.currentTransform; // W3C (future)
var imatrix = matrix.invertSelf(); // invert

// apply to point:
var x = pos.x * imatrix.a + pos.y * imatrix.c + imatrix.e;
var y = pos.x * imatrix.b + pos.y * imatrix.d + imatrix.f;

Update: I made a free solution (MIT) to embed all these steps into a single easy-to-use object that can be found here and also takes care of a few other nitty-gritty things most ignore.

Mouse Coordinates on Canvas after CSS scale

The mouse coordinates are in display pixels. To convert that to canvas coordinates, you'll need to scale them accordingly.

One way of doing this is:

const canvasX = mouseX * canvas.width / canvas.clientWidth;
const canvasY = mouseY * canvas.height / canvas.clientHeight;

as shown in this example:

const status = document.getElementById("coords");

const canvas = document.createElement("canvas");
canvas.width = 700;
canvas.height = 700;
document.body.appendChild(canvas);

canvas.addEventListener('mousemove', event => {
const mouseX = event.clientX - canvas.offsetLeft;
const mouseY = event.clientY - canvas.offsetTop;

// scale mouse coordinates to canvas coordinates
const canvasX = mouseX * canvas.width / canvas.clientWidth;
const canvasY = mouseY * canvas.height / canvas.clientHeight;

status.innerHTML = `${mouseX} | ${mouseY}<br>${canvasX} | ${canvasY}`;
});
canvas {
width:250px;
height:250px;
background-color:#f0f;
}
<div id="coords">??? | ???<br>??? | ???</div>

Getting the exact mouse position on canvas inside divisons

You need to scale the mouse coordinates to match the canvas resolution.

// your code
var mouseX = evt.clientX - left + window.pageXOffset;
var mouseY = evt.clientY - top + window.pageYOffset;

// Add the following 3 lines to scale the mouse coordinates to the
// canvas resolution
const bounds = canvas_test.getBoundingClientRect();
mouseX = (mouseX / bounds.width) * canvas_test.width;
mouseY = (mouseY / bounds.height) * canvas_test.height;

// your code
return {
x:mouseX,
y:mouseY
};

How can I get the rotated coordinates of a mouse-click on a canvas?

If you're only rotating in 90 degree increments then the math is fairly straight forward. This example rotates coordinates 90° to the left.

unrotated.x = anchor.y;
unrotated.y = s.height - anchor.x;

Other rotations are similar, just swapping in and out x and y positions and widths and heights.



Related Topics



Leave a reply



Submit