Resize an image in Javascript

In one project I was working on recently I needed to mimic the behaviour of css background cover in a canvas and I think it could be useful to share my implementation. While I think it is always a good idea to use CSS when possible I found myself in a situation where I wasn’t able to, as I needed to send to an iOS app a screenshot of what was happening on a portion of an HTML page displaying a canvas with three.js.
I could have taken the screenshot natively on iOS, and if I needed the entire screen I’d have gone for that, but since I was interested in only a part of the page I thought it would have been better to use Javascript running on the page, so the native code wouldn’t need to know about the HTML page displayed into a WKWebView. The screenshot can be transferred to the native application by converting the canvas to a base64 image, that’s what the toDataURL does.

Draw on canvas

Let’s start with something simple: how do you draw an image in javascript? You can use a canvas and place it into your DOM.
https://www.w3schools.com/html/html5_canvas.asp


var myCanvas = document.createElement("canvas");
document.body.appendChild(myCanvas);
myCanvas.width = screen.width;
myCanvas.height = screen.height;

var ctx = myCanvas.getContext('2d');
var backgroundImg = new Image;
backgroundImg.onload = function() {
    ctx.drawImage(backgroundImg, 0, 0);
    backgroundImg.src = backgroundImagePath;
}

First you need to create the canvas element, then get its context and once you have created an image you can draw it into the context.
If you call myCanvas.toDataURL(“image/jpeg”, 0.9) you get a base64 jpeg representation of your canvas.

Center an image

Let’s get back to my original problem: I want to make a screenshot of a portion of the screen where I set a background image. The image is bigger than the canvas, so in CSS I’d set it to be a background image with the “cover” style. But I’m using a canvas and if I set the image to be a background of its DOM element I won’t be able to take a screenshot of that via toDataURL, as it would capture only stuff I draw on the canvas. Since I can’t use CSS I have to draw the image in canvas.


function drawImageCenter(ctx, img) {
    var ratio = img.width / img.height;
    var width = ctx.canvas.width;
    var height = width / ratio;
    if (height < ctx.canvas.height) {
      height = ctx.canvas.height;
      width = height * ratio;
    }
    var x = width > ctx.canvas.width ? (ctx.canvas.width - width) / 2 : 0;
    var y = height > ctx.canvas.height ? (ctx.canvas.height - height) / 2 : 0;
    ctx.drawImage(img, x, y, width, height);
}

First I get the aspect ratio, image width divided by image height, then I se the width to match the canvas width and the height to be on the same aspect ratio as the original. If the height is too big for the canvas I have to do the opposite, so the height matches the canvas and the width is set accordingly. The last thing to do is compute the x and y offsets so the image is centred.