0

I have an image on Canvas. After rendering an image, I rotate the canvas to 90 degrees clockwise and then have to scale to fit the canvas.

Scaling an image to fit on canvas Resize image and rotate canvas 90 degrees

there is so many solutions available but unfortunately none of them worked for me yet

This is what I have right now.

width = 300; // after rotation
                    height = 400; // after rotation
                    var scale = width / img.height; // how much to scale the image to fit

                    console.log(scale);
                    canvas.width = width;
                    canvas.height = height;
                    ctx.setTransform(
                         0, scale, // x axis down the screen
                        -scale, 0, // y axis across the screen from right to left
                        width,    // x origin is on the right side of the canvas
                        0         // y origin is at the top
                    );
                    ctx.drawImage(img, 0, 0);
                    ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default

Currently, canvas is rotating fine but image is not fitting or scaling properly according to canvas width and height. Original image is large, I want to scale it to fit 300x400 on canvas.

Original Image - enter image description here

Actual result image looks like this which is not scaling full image -

enter image description here

newdeveloper
  • 1,401
  • 3
  • 17
  • 43

1 Answers1

3

To fit an image to a display area, use the min scale to fit height or fit width.

// size of canvas
const width = 100; 
const height = 298;

// image is assumed loaded and ready to be drawn

// Get min scale to fit
var scale = Math.min(width / image.width , height / image.height);

// you can also scale to fill using max
// var scale = Math.max(width / image.width , height / image.height);

canvas.width = width;
canvas.height = height;

// // draw image from top left corner
ctx.setTransform(scale, 0, 0, scale, 0, 0);
ctx.drawImage(image, 0, 0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default

Update

Re comment

As you are having trouble, and I am still not sure what you what the image to do, here is a general purpose function to rotate image in 90 deg steps, and scales to fit, fill, and natural. (Demo version in bottom snippet a little less verbose)

// img image to draw
// rot 1 unit = 90 deg. scaleType fit or fill
function drawRotatedImage(img, rot = 1, scaleType = "fit") {
  const w = img.naturalWidth;
  const h = img.naturalHeight;

  // direction of xAxis
  var xAxisX = Math.cos(rot * Math.PI / 2);
  var xAxisY = Math.sin(rot * Math.PI / 2);

  // modified transform image width and height to match the xAxis
  const tw = Math.abs(w * xAxisX - h * xAxisY);
  const th = Math.abs(w * xAxisY + h * xAxisX);

  var scale = 1; 
  if (scaleType === "fit") {
    scale = Math.min(canvas.width / tw, canvas.height / th);
  } else if (scaleType === "fill") {
    scale = Math.max(canvas.width / tw, canvas.height / th);
  }

  xAxisX *= scale;
  xAxisY *= scale;

  // Rotate scale to match scaleType . Center on canvas
  ctx.setTransform(xAxisX, xAxisY, -xAxisY, xAxisX, canvas.width / 2, canvas.height / 2);

  // Image drawn offset so center is at canvas center
  ctx.drawImage(img, -w / 2, -h / 2, w, h);
}

And to make sure the following is a tested runing example using your image.

const canWidth = 300;
const canHeight = 400;
const rot = 1;           
const scaleType = "fit"; 
const PI90 = Math.PI / 2;

function drawRotatedImage(img, rot, scale) {
  ctx.setTransform(1, 0, 0, 1, 0, 0); 
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  const w = img.naturalWidth, h = img.naturalHeight;
  var xAX = Math.cos(rot *= PI90 ), xAY = Math.sin(rot);
  const tw = Math.abs(w * xAX - h * xAY);
  const th = Math.abs(w * xAY + h * xAX);
  scale = Math[scale === "fit" ? "min" : "max" ](canvas.width / tw, canvas.height / th);
  xAX *= scale;
  xAY *= scale;
  ctx.setTransform(xAX, xAY, -xAY, xAX, canvas.width / 2, canvas.height / 2);
  ctx.drawImage(img, -w / 2, -h / 2, w, h);
}









// Stuff for demo and test. unrelated to answer.


const ctx = canvas.getContext("2d");  
// size canvas
canvas.width = canWidth;
canvas.height = canHeight;
const img = new Image;
const tests = [[0,"fit"], [1,"fit"], [2,"fit"], [3,"fit"],[0,"fill"], [1,"fill"], [2,"fill"], [3,"fill"]];

tests.clicker = 0;
img.src = "https://i.stack.imgur.com/SQmuv.jpg";
img.addEventListener("load", () => {
  info.textContent = ((rot % 4) * 90) + "deg CW scaled to " + scaleType.toUpperCase() + " click canvas to rotate and switch scales";
  drawRotatedImage(img, rot, scaleType)
});


canvas.addEventListener("click",() => {
  const test = tests[tests.clicker++ % tests.length];
  info.textContent = ((test[0] % 4) * 90) + "deg CW scaled to " + test[1].toUpperCase();
  drawRotatedImage(img, ...test)
});
body {
   font-family: "arial black";
}
canvas {
  border: 1px solid black;
  position: absolute;
  top: 30px;
  left: 10px;    
}
#info {
  position: absolute;
  top: 2px;
  left: 10px;
}
<canvas id="canvas"></canvas>

<div id="info"></div>
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • I have to rotate the image clockwise first so setTransform mentioned above will not work. the one in my example rotates the image correctly. using min scale or max scale also not seem to be working. it still cut the image. my desired width is 300 and height is 400 – newdeveloper Sep 12 '19 at 02:24
  • those are some very useful snippets and will help lot more people in future. it solved my issue. Image is scaling and fitting to canvas now. thanks – newdeveloper Sep 12 '19 at 14:32
  • I need one more favor, how to improve quality of the image after scale to fit as above is processes? Currently my 880KB image reduced to 501Kb. it shows blurry right now. trying http://jsfiddle.net/9g9Nv/442/ and http://jsfiddle.net/9g9Nv/96/ – newdeveloper Sep 12 '19 at 21:04
  • above 2 jsfiddle improves the quality but not sure how to make it work with 90 degree rotation solution that we have above? basically i want to use one of the jsfiddle and also transform the image to 90degrees clock wise. – newdeveloper Sep 12 '19 at 21:05
  • could you please guide me on how to rotate canvas to 90 degrees clockwise into this jsfiddle. http://jsfiddle.net/9g9Nv/442/ This improves image quality but not able to figure our rotation. – newdeveloper Sep 12 '19 at 21:55