What i found (for Chrome and Internet Explorer) is the very tricky, subtle, differences between
canvas.width
, and
canvas.style.width
They are not interchangeable. If you want to manipulate them you must do so very carefully to achieve what you want. Some values are measured in physical pixels. Others are measured in logical pixels (the pixels if the screen was still zoomed to 96 dpi).
In my case i want the Canvas
to take up the entire window.
First get the size of the window in logical pixels (which i call "CSS pixels"):
//get size in CSS pixels (i.e. 96 dpi)
var styleWidth = window.innerWidth; //e.g. 420. Not document.body.clientWidth
var styleHeight = window.innerHeight; //e.g. 224. Not document.body.clientHeight
These values are in "logical pixels". When my browser is zoomed in, the amount of "logical pixels" decreases:
Zoom window.innerWidth x windows.innerHeight
==== ======================================
100% 1680 x 859
200% 840 x 448
400% 420 x 224
75% 2240 x 1193
What you then have to do is figure out the "real" size of the window; applying a zoom correction factor. For the moment we'll abstract the function that can figure out the current zoom level (using the TypeScript syntax):
function GetZoomLevel(): number
{
}
With the GetZoomLevel()
function, we can calculate the real size of the window, in "physical pixels". When we need to set the width
and height
of the canvas to the size of the window in physical pixels:
//set canvas resolution to that of real pixels
var newZoomLevel = GetZoomLevel();
myCanvas.width = styleWidth * newZoomLevel; //e.g. 1680. Not myCanvas.clientWidth
myCanvas.height = styleHeight * newZoomLevel; //e.g. 859. Not myCanvas.clientHeight
The critical trick is that internally the canvas will render to a surface that is width
by height
pixels in size. After that rendering is complete, CSS can come along and shrink or stretch your canvas:
- blurring it if CSS makes it larger
- throwing away pixel data if CSS makes it smaller.
The final piece is to resize the canvas so that it takes up the entire client area window, using CSS length String:
myCanvas.style.width = styleWidth + "px"; //e.g. "420px"
myCanvas.style.height = styleHeight + "px"; //e.g. "224px"
So the canvas will be positioned and sized correctly in your browser window, yet internally is using full "real" pixel resolution.
GetZoomLevel()
Yes, you need the missing piece, GetZoomLevel
. Unfortunately only IE supplies the information. Again, using TypeScript notation:
function GetZoomLevel(): number {
/*
Windows 7, running at 131 dpi (136% zoom = 131 / 96)
Internet Explorer's "default" zoom (pressing Ctrl+0) is 136%; matching the system DPI
screen.deviceYDPI: 130
screen.systemYDPI: 131
screen.logicalYDPI: 96
Set IE Zoom to 150%
screen.deviceYDPI: 144
screen.systemYDPI: 131
screen.logicalYDPI: 96
So a user running their system at 131 dpi, with IE at "normal" zoom,
and a user running their system at 96 dpi, with IE at 136% zoom,
both need to see the same thing; everything zoomed in from our default, assumed, 96dpi.
http://msdn.microsoft.com/en-us/library/cc849094(v=vs.85).aspx
Also note that the onresize event is triggered upon change of zoom factor, therefore you should make sure
that any code that takes into account DPI is executed when the onresize event is triggered, as well.
http://htmldoodads.appspot.com/dimensions.html
*/
var zoomLevel: number;
//If the browser supports the corrent API, then use that
if (screen && screen.deviceXDPI && screen.logicalXDPI)
{
//IE6 and above
zoomLevel = (screen.deviceYDPI / screen.logicalYDPI);
else
{
//Chrome (see http://htmldoodads.appspot.com/dimensions.html)
zoomLevel = window.outerWidth / window.innerWidth; //e.g. 1680 / 420
}
return zoomLevel;
}
Unfortunately no browsers besides IE support telling you:
My full TS code is:
function OnWindowResize() {
if (drawingTool == null)
return;
//Zoom changes trigger window resize event
//get size in CSS pixels (i.e. 96 dpi)
var styleWidth = window.innerWidth; //myCanvas.clientWidth;
var styleHeight = window.innerHeight; //myCanvas.clientHeight;
var newZoomLevel = GetZoomLevel();
// myCanvas.width = document.body.clientWidth;
// myCanvas.height = document.body.clientHeight;
//set canvas resolution to that of real pixels
myCanvas.width = styleWidth * newZoomLevel;
myCanvas.height = styleHeight * newZoomLevel;
//myCanvas.clientWidth = styleWidth;
//myCanvas.clientHeight = styleHeight;
myCanvas.style.width = styleWidth + "px"; //styleWidth.toString() + "px";
myCanvas.style.height = styleHeight + "px"; //styleHeight.toString() + "px";
drawingTool.Width = myCanvas.width;
drawingTool.Height = myCanvas.height;
// Only take action if zoom factor has changed (won't be triggered by actual resize of window)
if (newZoomLevel != lastZoomLevel) {
lastZoomLevel = newZoomLevel;
drawingTool.CurrentScaleFactor = newZoomLevel;
}
}
It's also important to tell your drawing code the user's current zoom level, in case you try to hard-code any lengths. E.g. attempting to draw
a 32px box at (50,50)
is wrong. You need to draw a
128 px box at (200,200)
when the zoom factor is 4.
Note: Any code is released into the public domain. No attribution required.