Skeletor Skeletor -4 years ago 142
Javascript Question

How can i move rotated canvas image whitout disorientation?

I have an image loaded into the canvas. I can't (drag) move the image properly, after it is rotated. Actually, it moves but it moves based on the image's coordinate plane. So moving a 90 degree rotated image to the right, moves down and not to the right as expected. What could be a good way to solve this problem?

enter image description here

This is my draw function:

function draw(){
var im_width = parseInt( imageObj.width + resizeAmount );
var im_height = parseInt( imageObj.height + resizeAmount );
var rotationAmount = rotationVal - prevRotation;
prevRotation = rotationVal;

context.clearRect( 0, 0, canvas.width, canvas.height );
context.translate( canvas.width/2, canvas.height/2 );
context.rotate( rotationAmount * Math.PI / 180 );
context.translate( -canvas.width/2, -canvas.height/2 );
context.drawImage( imageObj, moveXAmount, moveYAmount, im_width, im_height );

Here is the jsdiddle where you can simulate it and see what I mean.

PS: You can rotate the image with the slider on the left. The bottom slider is for zooming. Please re-run the fiddle if the image does not appear in the first load.

Answer Source

Reset the transformations after drawing. The mouse events are transformed as well when applied to the canvas context so using transforms only when drawing can solve this. However, this also require the code to use absolute values, for example:

function draw(){
    var im_width = parseInt( imageObj.width + resizeAmount, 10 );
    var im_height = parseInt( imageObj.height + resizeAmount, 10 );
    var rotationAmount = rotationVal; // disabled: - prevRotation;

    context.clearRect( 0, 0, canvas.width, canvas.height );

    // move to origin first
    context.translate( moveXAmount, moveYAmount );

    // rotate
    context.rotate( rotationAmount * Math.PI / 180 );

    // change to translate back based on image size
    // (remember to compensate for scale, not shown here)
    context.translate( -imageObj.width/2, -imageObj.height/2 );
    context.drawImage( imageObj, 0, 0, im_width, im_height );

    // reset transforms (identity matrix)

Modified fiddle

Optionally you would need to use inverse matrix. This is something that will become available later when we can take out a SVGMatrix based on the current transformation matrix, but this is not widely available at this time. Otherwise the inverse matrix would be applied to the mouse x/y position to sort of, as the name implies, inverse the effect of the main transformation.

Optionally use a custom transformation matrix solution to keep track of transform (I'll invite you to check out my own approach to this here, it's free).

PS: also fixed the image loading problem (see fiddle).

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download