HTML5 canvas translate back after scale and rotation

Viewed 3952

I'm trying to do a few thing with canvas. First I have a user upload an image, if the image is larger than I want I need to scale it down. That part is working just fine. Recently we ran into an issue with iPhone users uploading images. These have orientation issues. I've figured out how to get the orientation extracted, my issue is what happens when I manipulate the image in the canvas.

This is what I need to do: Get the image, translate(), scale(), rotate(), translate() <- get it back to its original position, drawImage().

When I do that part of the image is off in the abyss.

if (dimensions[0] > 480 || dimensions[1] > 853) {
    // Scale the image.
    var horizontal = width > height;
    if (horizontal) {
        scaledHeight = 480;
        scaleRatio = scaledHeight / height;
        scaledWidth = width * scaleRatio;
    } else {
        scaledWidth = 640;
        scaleRatio = scaledWidth / width;
        scaledHeight = height * scaleRatio;
    }

    canvas['width'] = scaledWidth;
    canvas['height'] = scaledHeight;
    ctx['drawImage'](image, 0, 0, width, height, 0, 0, scaledWidth, scaledHeight);
    /* Rotate Image */
    orientation = 8; //manual orientation -> on the site we use loadImage to get the orientation
    if(orientation != 1){
        switch(orientation){
            case 8:
            case 6:
                canvas.width = scaledHeight;
                canvas.height = scaledWidth;
                break;
        }

        var halfScaledWidth = scaledWidth/2;
        var halfScaledheight = scaledHeight/2;

        ctx.save(); //<- SAVE

        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.translate(halfScaledWidth,halfScaledheight);
        switch(orientation){
            case 8: //rotate left
                ctx.scale(scaleRatio,scaleRatio);
                ctx.rotate(-90*Math.PI/180);
                ctx.translate(-1380,-1055); // <-Manuial numbers
                break;
            case 3: //Flip upside down
                ctx.scale(scaleRatio,scaleRatio);
                ctx.rotate(180*Math.PI/180);
                ctx.translate(-925,-595); //<-Manuial numbers
                break;
            case 6: //rotate right
                ctx.scale(scaleRatio,scaleRatio);
                ctx.rotate(90*Math.PI/180);
                ctx.translate(-462,-130); //<-Manuial numbers
                break;
        }

        //re-translate and draw image
        //ctx.translate(-halfScaledWidth,-halfScaledheight);
        ctx.drawImage(image,-halfScaledWidth, -halfScaledheight);
        ctx.restore(); //<- RESTORE
    }
    /* Rotate Image */
}

I have orientation set manually so I can see how it looks in each position Im worried about. If its a portrait orientation I flip the canvas.

I've tried save() and restore(). I've tried translate(x,y) then translate(-x,-y).. My guess is that because of the scale the grid is off and x and y need to be multiplied. I tried doing that against the scaleRatio and still didn't work.

As you can see I manually set the translate back but that only works with the image size I am working with, so not a good solution at all!

Here is the code: JSFiddle If I do a normal rotate right it all works.

Thanks!

3 Answers

My solution for this was using two canvases one for the rotation and scaling (around the center point of the image) then using a new canvas for translating and drawing the prev canvas on it. it worked perfectly for me:

const root = document.querySelector("#root");
const img = document.querySelector('#img');

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext("2d");

const tempCanv = document.createElement('canvas')
const tempCtx = tempCanv.getContext('2d');

canvas.height = img.height
canvas.width = img.width
tempCanv.width = img.width
tempCanv.height = img.height

const position = [0.25, 0.25];
const rotation = -40;
const scale = [37.222, 37.222];

const x = img.width / 2;
const y = img.height / 2;

// Set the origin to the center
tempCtx.translate(x, y);

// now rotate and scale around the center
tempCtx.rotate(Math.PI / 180 * rotation);
tempCtx.scale(scale[0] / 100, scale[1] / 100);

// Translate back to origin
tempCtx.translate(-x, -y);

// Draw temp
tempCtx.drawImage(img,0,0);

// Translate the final canvas
ctx.translate(position[0] * canvas.width, position[1] * canvas.height);
ctx.drawImage(tempCanv, 0, 0);
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #root{
      display: flex;
      gap: 2rem;
      padding: 1rem;
      overflow:visible
      position relative;
    }
    .image-container{
      
    }
    #img{
      transform: rotate(-40deg) scale(0.37222, 0.37222);
      top: 25%;
      left: 25%;
      position: relative;
    }
  </style>
</head>
<body>
  <div id="root">
  <div class="image-container">
     <img id="img" src="https://media.istockphoto.com/photos/cat-world-picture-id1311993425?b=1&k=20&m=1311993425&s=170667a&w=0&h=vFvrS09vrSeKH_u2XZVmjuKeFiIEjTkwr9KQdyOfqvg=" alt="red">
  </div>
   
    <canvas id="canvas"></canvas>
  </div>
</body>
</html>

Related