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.