Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5 canvas get coordinates after zoom and translate

enter image description hereenter image description here

BACKGROUND: I have an HTML5 canvas and I have an image drawn on it. Now when the image is first loaded, it is loaded at a scale of 100%. The image is 5000 x 5000. And the canvas size is 600 x 600. So onload, I only see the first 600 x-pixels and 600 y-pixels. I have the option of scaling and translating the image on the canvas.

MY ISSUE: I am trying to figure out an algorithm that return the pixel coordinates of a mouse click relative to the image, not the canvas while taking into account scaling and translating. I know there are a lot of topics already on this, but nothing I've seen has worked. My issue is when I have multiple translations and scaling. I can zoom once and get the correct coordinates, and I can then scale and get the right coordinates again, but once I zoom or scale more than once, the coordinates are off.

Here is what I have so far.

//get pixel coordinates from canvas mousePos.x, mousePos.y
(mousePos.x - x_translation)/scale //same for mousePos.y

annotationCanvas.addEventListener('mouseup',function(evt){
                     dragStart = null;
                     if (!dragged) {
                       var mousePos = getMousePos(canvas, evt);
                       var message1 =  " mouse  x: " + (mousePos.x) + '  ' + "mouse y: " + (mousePos.y);
                       var message =  "  x: " + ((mousePos.x + accX)/currentZoom*currentZoom) + '  ' + "y: " + ((mousePos.y + accY)/currentZoom);
                       console.log(message);
                       console.log(message1);
                       console.log("zoomAcc = " + zoomAcc);
                       console.log("currentZoom = " + currentZoom);
                       ctx.fillStyle="#FF0000";
                       ctx.fillRect((mousePos.x + accX)/currentZoom, (mousePos.y + accY)/currentZoom, -5, -5);

                     }
             },true);
//accX and accY are the cumulative shift for x and y respectively, and xShift and xShift yShift are the incremental shifts of x and y respectively

where current zoom is the accumulative zoom. and zoomAcc is the single iteration of zoom at that point. So in this case, when I zoom in, zoomAcc is always 1.1, and currentZoom = currentZoom*zoomAcc.

Why is this wrong? if someone can please show me how to track these transformations and then apply them to mousePos.x and mousePos.y I would be grateful.

thanks

UPDATE:

In the image, the green dot is where I clicked, the red dot is where my calculation of that point is calculated, using markE's method. The m values are the matrix values in your markE's method.

like image 450
flash Avatar asked Feb 12 '14 01:02

flash


1 Answers

When you command the context to translate and scale, these are known as canvas transformations.

Canvas transformations are based on a matrix that can be represented by 6 array elements:

// an array representing the canvas affine transformation matrix 
var matrix=[1,0,0,1,0,0];

If you do context.translate or context.scale and also simultaneously update the matrix, then you can use the matrix to convert untransformed X/Y coordinates (like mouse events) into transformed image coordinates.

context.translate:

You can simultaneously do context.translate(x,y) and track that translation in the matrix like this:

// do the translate
// but also save the translate in the matrix
function translate(x,y){
    matrix[4] += matrix[0] * x + matrix[2] * y;
    matrix[5] += matrix[1] * x + matrix[3] * y;
    ctx.translate(x,y);
}

context.scale:

You can simultaneously do context.scale(x,y) and track that scaling the matrix like this:

// do the scale
// but also save the scale in the matrix
function scale(x,y){
    matrix[0] *= x;
    matrix[1] *= x;
    matrix[2] *= y;
    matrix[3] *= y;    
    ctx.scale(x,y);
}

Converting mouse coordinates to transformed image coordinates

The problem is the browser is unaware that you have transformed your canvas coordinate system and the browser will return mouse coordinates relative to the browser window--not relative to the transformed canvas.

Fortunately the transformation matrix has been tracking all your accumulated translations and scalings.

You can convert the browser’s window coordinates to transformed coordinates like this:

// convert mouseX/mouseY coordinates
// into transformed coordinates

function getXY(mouseX,mouseY){
    newX = mouseX * matrix[0] + mouseY * matrix[2] + matrix[4];
    newY = mouseX * matrix[1] + mouseY * matrix[3] + matrix[5];
    return({x:newX,y:newY});
}
like image 62
markE Avatar answered Oct 12 '22 15:10

markE