The HTML5Rocks article related to the question makes things more difficult than they should be, but it makes the same basic mistake as the other resources I saw ( 1 , 2 , 3 , 4 ). These links give some variation on this formula:
var rect = canvas.getBoundingClientRect(); canvas.width = Math.round (devicePixelRatio * rect.width);
The formula is incorrect. Best formula
var rect = canvas.getBoundingClientRect(); canvas.width = Math.round (devicePixelRatio * rect.right) - Math.round (devicePixelRatio * rect.left);
The fact is that it makes no sense to scale the width or height (i.e. the difference in two positions) using devicePixelRatio. You should only scale the absolute position. I cannot find a link to this exact point, but I think it is obvious as soon as you receive it.
Sentence.
It is not possible to calculate the physical width and height of a rectangle (in device pixels) from its width and CSS height (in pixels independent of the device).
Evidence.
Suppose you have two elements bounding rectangles in device-independent pixels
{ left: 0, top: 10, right: 8, bottom: 20, width: 8, height: 10 }, { left: 1, top: 20, right: 9, bottom: 30, width: 8, height: 10 }.
Now, assuming devicePixelRatio is 1.4, the elements will span these device pixel rectangles:
{ left: 0, top: 14, right: 11, bottom: 28, width: 11, height: 14 }, { left: 1, top: 28, right: 13, bottom: 42, width: 12, height: 14 },
where left, top, right, and bottom were multiplied by devicePixelRatio and rounded to the nearest integer (using Math.round ()).
You will notice that the two rectangles have the same width in device-independent pixels, but differ in width in the device pixels. β―
Testing.
Here is sample code for testing. Download it in a browser, then zoom in and out with the mouse. There should always be clear lines on the last canvas. The other three will be blurred at some resolutions.
Tested on desktop Firefox, IE, Edge, Chrome and Android Chrome and Firefox. (Note that this does not work on JSfiddle , because getBoundingClientRect returns invalid values ββthere.)
<!DOCTYPE html> <html> <head> <script> function resize() { var canvases = document.getElementsByTagName("canvas"); var i, j; for (i = 0; i != canvases.length; ++ i) { var canvas = canvases[i]; var method = canvas.getAttribute("method"); var dipRect = canvas.getBoundingClientRect(); var context = canvas.getContext("2d"); switch (method) { case "0": </script> </head> <body> <canvas method="0" style="position: absolute; left: 1px; top: 10px; width: 8px; height: 10px"></canvas> <canvas method="1" style="position: absolute; left: 1px; top: 25px; width: 8px; height: 10px"></canvas> <canvas method="2" style="position: absolute; left: 1px; top: 40px; width: 8px; height: 10px"></canvas> <canvas method="3" style="position: absolute; left: 1px; top: 55px; width: 8px; height: 10px"></canvas> </body> </html>