Resizing HTML5 Canvas and Scaling Sprites
I'm working on a game using HTML5/JavaScript and I ran into a problem: My sprites were created as 8x8px and that would be way too small to view on high resolution displays. Not only that, I wanted the size of the canvas to adapt to whatever display it was being viewed on without distorting my sprites, and I didn't want the canvas to take up the entire window.
I wrote a sample to show how to go about scaling the canvas to your browser window up to a max height/width, which you can view on codepen or clone from github. I'll explain it below:
First, create a <canvas>
inside a container <div>
.
<div id="game">
<canvas id="canvas"></canvas>
</div>
Next, you'll need to add some styling to get the canvas to stay in the center. Remove margin from <body>
and <html>
and make it take up 100% of the window. Then add flexbox to your container <div>
and give it a height and width of 100% as well.
html,
body {
margin: 0;
height: 100%;
width: 100%;
}
#game {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
Now get a reference to your <canvas>
and canvas context, and set the initial width and height. SIZE
refers to the sprite's size, which is 8 pixels wide by 8 pixels tall. map
refers to my 2D array. How to create the map is out of the scope of this post, but you can check out the source code to see how it's done.
const ctx = document.getElementById('canvas').getContext('2d');
// size of each 'tile'
const SIZE = 8;
// native width and height (does not change)
const nHeight = SIZE * map.length;
const nWidth = SIZE * map[0].length;
function render() {
tiles.forEach(tile => {
ctx.drawImage(tilesheet, tile.srcX, tile.srcY, SIZE, SIZE, tile.x, tile.y, SIZE, SIZE)
})
}
window.addEventListener('load', () => {
ctx.canvas.width = nWidth;
ctx.canvas.height = nHeight;
buildMap();
render();
})
At this point your canvas is going to be very tiny since it's using your native game resolution (which, in this case is 56x40). Your native game resolution does not change. It's used for your game's logic, such as character movement, collision, etc.
In order to scale your canvas you'll want to keep track of a separate width and height for your canvas resolution. If you don't want your canvas to extend to the width/height of the window you can add your own max width/height and/or add a percentage of the window that you want to be taken up. You can play around with this; you might not need or want both or either of these settings.
// I just picked 20 at random here.
// In this instance maxWidth = 56 * 20 = 1,120 and maxHeight = 40 * 20 = 800
const maxMultiplier = 20;
const maxWidth = nWidth * maxMultiplier;
const maxHeight = nHeight * maxMultiplier;
// % of browser window to be taken up by the canvas
// this can just be set to 1 if you want max height or width
const windowPercentage = 0.9;
// the canvas' displayed width/height
// this is what changes when the window is resized
// initialized to the native resolution
let cHeight = nHeight;
let cWidth = nWidth;
Now when you load, you want to set your canvas dimensions to the variables you're using for your canvas resolution as opposed to your native resolution. You also want to set up an event listener to watch for resize
, and in there you want to handle your canvas resizing as well as re-render.
window.addEventListener('load', () => {
// initialize native height/width
ctx.canvas.width = cWidth;
ctx.canvas.height = cHeight;
buildMap();
resize();
render();
})
window.addEventListener('resize', () => {
resize();
render();
})
function resize() {
cWidth = window.innerWidth;
cHeight = window.innerHeight;
// ratio of the native game size width to height
const nativeRatio = nWidth / nHeight;
const browserWindowRatio = cWidth / cHeight;
// browser window is too wide
if (browserWindowRatio > nativeRatio) {
cHeight = Math.floor(cHeight * windowPercentage); // optional
if (cHeight > maxWidth) cHeight = maxHeight; // optional
cWidth = Math.floor(cHeight * nativeRatio);
} else {
// browser window is too high
cWidth = Math.floor(cWidth * windowPercentage); // optional
if (cWidth > maxWidth) cWidth = maxWidth; // optional
cHeight = Math.floor(cWidth / nativeRatio);
}
// set the canvas style width and height to the new width and height
ctx.canvas.style.width = `${cWidth}px`;
ctx.canvas.style.height = `${cHeight}px`;
}
In resize()
, you first set cWidth
and cHeight
(the canvas resolution variables) to the window's innerWidth
and innerHeight
. Then you need to get the ratio of width to height for both your native resolution and your browser window. If the ratio of the window's innerWidth
/ innerHeight
(cWidth/cHeight
) is greater than the native resolution ratio (nWidth/nHeight
) then the browser is too wide and you need to recalculate the width so it meets the correct ratio. Otherwise, the browser window is too high and you need to recalculate the height.
If you want to set a max window percentage you can set the new canvas width/height by first multiplying their values by maxPercentage
. If you want to use a maxWidth/maxHeight you can check if the cWidth
and cHeight
values are greater, and if so, set them to their max. After, or instead of, all of that, you can set the final new width or height values (depending on which condition you hit). The new cWidth
is calculated by multiplying cHeight
by nativeRatio
and the new cHeight
is calculated by dividing cWidth
by nativeRatio
.
Next, set the canvas' style property's width and height to the new cWidth
and cHeight
. "Canvas.width and canvas.height set the size of the canvas. canvas.style.width and canvas.style.height set the resolution."
Finally, add image-rendering: pixelated
to your canvas
styles so that your pixel art isn't blurry.
canvas {
image-rendering: pixelated;
}
That should be it. I know I assumed a lot of prior knowledge with canvas and setting up sprites and the game map, so if you're struggling leave a comment and maybe I, or another reader, can help!
Here are some links if you're interested in reading or watching more about this topic.
- 28- Making HTML5 Game: Resizing Game Part 2. Javascript Tutorial Guide
- Advanced Game Design with HTML5 and JavaScript by Rex van der Spuy
- Building JavaScript Games for Phones, Tablets, and Desktop by Arjan Egges
- Scaling a Javascript Canvas Game Properly
Top comments (0)