2

These questions are similar but do not help: this, this, this, and this.

The goal is to draw an image onto a square canvas while preserving the original aspect ratio and centering the image if the original aspect ratio is not square.

For instance, take the attached 1262x2688 image. The code below resizes this to 100x100, but it distorts the aspect ratio.

The code should: (1) scale the image to fit the 100x100 canvas; (2) preserve the aspect ratio; and (3) center the image vertically and horizontally within the canvas.

    // Create canvas element.
    var canvas = $(document.createElement("canvas"));

    // Get canvas context.
    var context = canvas[0].getContext("2d");

    // Set canvas size.
    canvas[0].width = 100;
    canvas[0].height = 100;

    // Write image to canvas.
    context.drawImage(image, 0, 0, newWidth, newHeight);

Image

enter image description here

Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • 1
    If you want to keep the entire image without it being cut off, this is impossible. You have to keep the aspect ratio in order for the image to not be distorted, but if it's ok to cut part of the image off then it is possible. – Travis Delly Oct 08 '19 at 00:10
  • Travis is correct. You can't change the aspect ratio of an image without distorting it. You either need to determine which is bigger (width or height) and use the largest one to fill the canvas and center it, leaving space between borders, or you need to partially cut the image in order to make it square. – icecub Oct 08 '19 at 00:17
  • @icecub sorry for the confusion. the image should be scaled to fit within the square canvas while preserving the aspect ratio. so the image won't end up square, but will retain its aspect ratio (using the smallest of the two scale factors) and will need to get centered horizontally or vertically. – Crashalot Oct 08 '19 at 06:17

3 Answers3

4

Here's the code we used:

    // Create canvas element.
    var canvas = $(document.createElement("canvas"));

    // Get canvas context.
    var context = canvas[0].getContext("2d");

    // Set canvas size.
    canvas[0].width = canvasWidth;
    canvas[0].height = canvasHeight;

    // Set image size, must use image.naturalWidth and image.naturalHeight -- not image.width and image.height.
    const imageWidth = image.naturalWidth;
    const imageHeight = image.naturalHeight;

    // Set scale to fit image to canvas, 
    const scale = Math.min(canvasWidth/imageWidth, canvasHeight/imageHeight);

    // Set new image dimensions.
    const scaledWidth = imageWidth * scale;
    const scaledHeight = imageHeight * scale;

    // Draw image in center of canvas.
    context.drawImage(image, (canvasWidth - scaledWidth)/2, (canvasHeight - scaledHeight)/2, scaledWidth, scaledHeight);
Crashalot
  • 33,605
  • 61
  • 269
  • 439
2

To fit an image to a canvas while preserving the aspect use the following

const w = image.naturalWidth;
const h = image.naturalHeight;

// Get the min scale to fit the image to the canvas
const scale = Math.min(canvas.width / w, canvas.height / h);

// Set the transform to scale the image, and center to the canvas
ctx.setTransform(scale, 0, 0, scale, canvas.width / 2, canvas.height / 2);

// draw the image offset by half its width and height to center and fit
ctx.drawImage(image, -w / 2, -h / 2, w, h);

// to reset the transform
// ctx.setTransform(1,0,0,1,0,0);
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • Thanks for this code! We wound up using something different, but your code helped. :) – Crashalot Oct 13 '19 at 00:39
  • @Crashalot If you use `setTranform(xAxisXScale, xAxisYscale, yAxisXScale, yAxisYScale, canvasXOrigin, canvasYOrigin)` you do not need to do the additional calculations to translate and scale the image (as you do in your answer). You can render to the canvas in the image coordinates – Blindman67 Oct 13 '19 at 01:51
  • We used your version originally, but it didn't work when the canvas was smaller than the image. If you update your answer, it would be great to award you the points! Thanks again for your help. – Crashalot Oct 13 '19 at 05:41
  • @Crashalot I am not concerned about points, just thought it odd that you used a more complex solution. The code in the answer does work, I can see no typos, If it did not work for smaller canvas then neither would it work for larger canvas – Blindman67 Oct 13 '19 at 07:33
  • I've just used this exact code to draw various 1000-2000px wide/high images onto a 400x400px square canvas (as a preview thumbnail), and it seems to work perfectly for me. – Mark Bell Dec 23 '22 at 12:31
0

Assuming I've got the calculations the right way round, the following might work?

ratio = image.width/image.height;
if (image.width > image.height) {
    output.height = ( 100 / ratio ) 
    output.width = 100
    output.x = 0
    output.y = (100 - output.height) / 2
} else {
    output.height = 100
    output.width = 100 * ratio
    output.x = (100 - output.width) / 2
    output.y = 0
}
NickSlash
  • 4,758
  • 3
  • 21
  • 38