How to properly set size of the Canvas element
A problem that comes up frequently at Q&A sites and in forums is how to properly set size of the canvas element. The problems are typical: things doesn’t draw where they are suppose to show up, or doesn’t show at all or they gets stretched or everything looks blurry.
For someone new to canvas this is an understandable frustration: the logic dictates, especially if you have only worked with HTML/CSS, that in order to set a size of an element (for example a DIV) you need to use CSS:
#myDiv {
width: 200px;
height: 100%;
}
Then this should also be the case for other elements including canvas, right?
You can do this with Canvas too however, canvas is not your everyday element. It’s more comparable to an image element than a regular element, but a “live” image (as we can change its content and size).
The canvas consists roughly put of two layers that is the element itself that we use in HTML – the CANVAS – and there is the (source) bitmap which we deal with mostly through the context (which is also a layer/API) and JavaScript.
The catch
If we don’t set any actual size for the canvas’ source bitmap it will default to 300 x 150 pixels as per specification. If you now set the CSS size of the element to lets say 900 x 450 pixels what happens is that those 300 x 150 pixels are simply scaled to the new size as the CSS applies to the element while the default 300 x 150 applies to the source bitmap (ie. the image). The applied CSS rule doesn’t do anything with the actual bitmap size.
It would be exactly the same if the canvas was an image, which works in a similar fashion: it has the image element and then the source bitmap – the image itself. If you choose to use a different size for the element than what the image is, the image is simply stretched but its original data stays the same. There are no more or less pixels in the original image.
How to set the size of the canvas bitmap
To set the size of the source bitmap of the canvas element, the bitmap we deal with when we add shapes and graphics, you simply use its two properties or attributes for this. From HTML we would do:
<canvas id="myCanvas" width="500" height="400"></canvas>
and in JavaScript we would do it by using properties:
var canvas = document.getElementById('myCanvas');
canvas.width = 500; /// always integer and in pixels
canvas.height = 400;
This will now give us the correct size of the source bitmap.
You can still add CSS to this but in most cases this is not necessary. There are special cases we will take a look at later but first lets see how we can deal with dynamic sizes.
How to dynamically resize canvas
So how can we make the canvas fit for example the window or a parent container, or follow it when we resize them if you should not use CSS for this?
To fit the canvas for window we simply set the size using the window’s
innerWidth and innerHeight:canvas.width = window.innerWidth; canvas.height = window.innerHeight;
We won’t be able to avoid CSS in this case as you would probably want to use CSS to place the canvas in the upper corner using absolute or fixed position, or to remove any margin from the body. You could also compensate the size by subtracting an amount from the variables.
But the essential part is that we need to set the size explicitly using JavaScript every time the canvas need to change size. That is typically not a problem as we cannot do anything on the canvas without JavaScript anyways.
If we want to update this size when we resize the window we simply set the new size on the window’s resize event:
/// set initial size using a common custom method
setCanvasSize();
/// subscribe to the resize event on window
window.addEventListener('resize', setCanvasSize, false);
/// method to set the canvas size
function setCanvasSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
One important note though: When you change the canvas’ bitmap size the canvas is cleared (as expected according to the specifications) so you will have to redraw everything in it. This you can do by having a generic update method in your script that takes care of all redrawing and updating.
For a canvas that sits inside a container element that changes size, we can use the
getComputedStyle method of the window object to get the parent element’s size in pixels (it always returns the size in pixels even if you used some other unit for it initially).
A little more code is required but this can be wrapped up in a generic function for reuse:
/// get parent element (assuming it exist)
var parent = canvas.parentElement;
/// get computed style
var cs = getComputedStyle(parent);
/// get size of parent in pixels and remove the 'px' at the end
/// and convert to integer -
var pw = parseInt(cs.getPropertyValue('width'), 10);
var ph = parseInt(cs.getPropertyValue('height'), 10);
/// set size of canvas to the same as the parent
canvas.width = pw;
canvas.height = ph;
It’s perhaps not the comfort you’ll get by setting-and-forgetting a dynamic unit (ie. %) with CSS but we are rarely using canvas in a passive way and these extra steps are re-usable and not really that much extra code.
Special cases
There are special cases where we do want to stretch the canvas element using CSS even if the result does not match the actual bitmap size. Particular for print usage where we probably want a higher resolution than the typical screen resolution.
Lets say we have some graphics drawn to the canvas and we want to print this as a high resolution sharp print. If we just used the size of the canvas as-is on the screen the result wouldn’t be any good on the print. We would need to use a much higher bitmap size to achieve for example the typical 300 DPI resolution for prints.
We can achieve this by providing a source bitmap in the final size we use for the print but CSS to scale it down to fit the size on the screen. The browser will internally scale these separately so it uses source bitmap size -> screen device, and when we print it uses source bimap size -> print device. So we don’t need to worry about how the canvas look on the screen when reduced by CSS.
Here is one way to create a bitmap for high-resolution printing using DPI and inches:
/// we have our canvas element in variable canvas var DPI = 300; /// use 300 "dots"-per-inches canvas.width = 10 * DPI; /// 10 inches wide @ 300 DPI canvas.height = 5 * DPI; /// 5 inches tall @ 300 DPI canvas.style.width = '900px'; /// use 900 pixels CSS width on screen canvas.style.height = '400px'; /// use 400 pixels CSS height on screen
This will give an appropriate bitmap size in pixels to represent the dots in the print (you could of course use any unit in the CSS size for the screen version).
Other cases can be when we need to scale for performance reasons or to provide zoom capability but this can also be achieved by using the scale transform of the canvas context or manually scaling the graphics by scaling the coordinates themselves. Chances are how ever that CSS will offer the fastest way of scaling but will also require more considerations.
When you are scaling the canvas element using CSS you must be aware of the following:
- The on-screen canvas may look low-res as the browser typically resizes its contents using bi-linear interpolation rather than bi-cubic. This means fine lines or points may or may not show on the screen (but they will on the print if used in this relation) or the content may look blurry or pixelated if it’s used to scale the canvas up in size.
- Everything you draw to it must be relative to the actual bitmap size not the CSS size.
- If you want to interact with the canvas using for example mouse you will have to scale all input coordinates manually. If the difference between the CSS size and actual bitmap size is huge this will result in very rough drawings (which you get around by providing zoom capability or by using more complex interpolation, curve smoothing and so forth to compensate for this).
- It uses more memory than an on-screen only canvas bitmap (much more in case of print purposes, but this is to be expected).
In conclusion
Eventually, more often than not, you will only need to concern yourself with setting the size by the canvas’ attributes or properties. But understanding how it works gives you the power to avoid the typical issues many are facing, or to use the canvas in a flexible way whether it is for performance or printing or other usages.
I hope this article shined some light on the differences between canvas and most other elements when it comes to set proper size and when to use CSS and not.
If you have special use cases or question don’t be afraid of leaving a comment below.
Nhận xét
Đăng nhận xét