Miyud Miyud - 3 months ago 16
CSS Question

Canvas on resize - keep a maximal width or height without stretching/padding

I have a question for a html5 game using canvas in javascript.

I would like that the canvas showed to the player contains either 1920 of canvas.width, either 1080 of canvas.height in order to not see the padding. That means that I don't want to keep the (optimal) game ratio 16/9 if the player resize his screen with an other ratio, but I want to keep the viewport ratio 16/9 to avoid stretching (using canvas.style.***)

Let me take an example of a player with a resizable screen (window.innerWidth = 500 and window.innerHeight = 1600), as 1600/500 > 1920 / 1080, I would like that the player can see 1920 of the game width and "less than 1080" of the game height, preventing viewport stretching.

Agar.io is an other good example.

Thank you very much !

Answer

To fit but will leave empty areas on the sides or top bottom if aspects do not match.

var scale = Math.min(innerWidth / canvas.width, innerHeight / canvas.height);

or to fill but will clip canvas if aspects do not match

var scale = Math.max(innerWidth / canvas.width, innerHeight / canvas.height);

for display 1600 by 500 and canvas 1920 by 1080

1600 / 1920 = 0.8333;
500 / 1080 = 0.463;

thus to fit canvas will be 0.463 * (1920,1080) = (889,500) empty on sides

and to fill canvas will be 0.8333 * (1920,1080) = (1600,900) clipped top and bottom.

More info on scale and fit can be found HTML5 canvas Scaling image to fit or fill

If you are scaling to fill the canvas you will need to account for the clipped area and find the offset to the top left corner of the canvas (this will be off the page).

var leftOffset = 0;
var topOffset = 0;
var canW = scale * canvas.width;
var canH = scale * canvas.height;
if(canW > innerWidth ){
    leftOffset = ((canW - innerWidth) / canW) * 1920 / 2;
}else
if(canH > innerHeight ){
    topOffset = ((canH - innerHeight) / canH) * 1080 / 2;
}

Your canvas will fill the page innerWidth and innerHeight but you will need to offset all rendering. This can be done by setting the transform to the correct offsets

ctx.setTransform(1,0,0,1,-leftOffset, -topOffset);

The canvas display size will be

canvas.style.width = innerWidth + "px"; 
canvas.style.height = innerHeight + "px";

and the canvas resolution will be

canvas.width = 1920 - (leftOffset * 2);
canvas.height = 1080 - (topOffset * 2);
Comments