[javascript] 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 a 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.

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to canvas

How to make canvas responsive How to fill the whole canvas with specific color? Use HTML5 to resize an image before upload Convert canvas to PDF Scaling an image to fit on canvas Split string in JavaScript and detect line break Get distance between two points in canvas canvas.toDataURL() SecurityError Converting Chart.js canvas chart to image using .toDataUrl() results in blank image Chart.js canvas resize

Examples related to html5-canvas

Scaling an image to fit on canvas Tainted canvases may not be exported How to fix getImageData() error The canvas has been tainted by cross-origin data? JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images Resize image with javascript canvas (smoothly) How to show a running progress bar while page is loading HTML5 Canvas Resize (Downscale) Image High Quality? HTML5 Canvas Rotate Image Real mouse position in canvas Changing three.js background to transparent or other color

Examples related to mouse-position

Real mouse position in canvas What is the difference between screenX/Y, clientX/Y and pageX/Y? Getting mouse position in c#