-1

I'm working on this website : sphere.mars2540.com.

Is there any way in javascript or css to force the canvas size not to scale regarding the zoom factor of the page and to constantly correspond to the real pixels of a screen (even 4k) ? I did know about devicePixelRatio, but it's not showing true ratio here on my 4k screen

thank you

2 Answers2

0

According to this answer it's not possible to disable page zooming, which is OK. What you can do instead and what would be actually better is to resize your canvas to match current page zoom (or actually make the canvas to be always the same size).

It's very simple, you just need to include window.devicePixelRatio in your canvas resize calculations.

The window.devicePixelRatio variable has value of 1 when there is no page zoom and changes on each zoom operation to match current zoom percentage.

For example to always match window dimensions you can do:

function resizeToWindow() {
    canvas.width = window.innerWidth * window.devicePixelRatio;
    canvas.height = window.innerHeight * window.devicePixelRatio;
}

To match parent container's size you can do:

function resizeToParent() {
    canvas.width = canvas.clientWidth * window.devicePixelRatio;
    canvas.height = canvas.clientHeight * window.devicePixelRatio;
}

With these or similar styles:

canvas {position: absolute; top: 0; left: 0; width: 100%; height: 100%}

The best place to detect is there need to call above functions and actually resize is to use requestAnimationFrame and compare current canvas dimensions and devicePixelRatio value with the values from last frame (do the check each frame, but don't resize each frame if there is no need).

Alternatively you could use the ResizeObserver API to obtain more accurate dimensions with the devicePixelContentBoxSize property, but browser support as of today is very limited.

HankMoody
  • 3,077
  • 1
  • 17
  • 38
  • Thank you hankMoody for your tips, but in my question I also mention the fact I need solution for "retina" and high dpi displays, do you know if it's possible to detect it ? because here on a 4k screen I get a 0.6 ratio for a 30% zoom. Edit: It seems that window.outerWidth/window.innerWidth does the trick. – Fabien Auréjac Nov 26 '21 at 21:13
  • Both retina and high DPI displays are represented by the same variable, ie. `devicePixelRatio`. On such screens `devicePixelRatio` will be greater than `1`. – HankMoody Nov 26 '21 at 21:16
  • Thank you, as I said in my comment edit, window.outerWidth/window.innerWidth is more accurate on my screen, I will look further if it works everywhere. – Fabien Auréjac Nov 26 '21 at 21:19
0

I managed to find a total answer in the context of webgl animation with THREE.js : using this trick I detect the ratio of zoom for the device :

function detectRatio() {
    var orientation=(screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation;
    if ("userAgentData" in window.navigator && window.navigator.userAgentData.mobile && orientation==="landscape-primary" || orientation==="landscape-secondary") {
        return window.devicePixelRatio;
    } else if ("userAgentData" in window.navigator && !window.navigator.userAgentData.mobile && orientation==="landscape-primary" || orientation==="landscape-secondary") {
        return window.outerWidth/window.innerWidth;
    } else if (orientation==="portrait-secondary" || orientation==="portrait-primary") {
        return window.devicePixelRatio;
    } else {
        return Math.max(window.devicePixelRatio, window.outerWidth/window.innerWidth);
    }
}
var ratio1=detectRatio();

Then I detect the change due to retina using this trick :

var ratio2=window.devicePixelRatio/ratio1;

Finally, I scale the canvas size with THREE.js api using for example :

renderer.setSize(width*ratio1*ratio2, height*ratio1*ratio2);

Todo -> Tada ! It's always on the max resolution.

  • I don't know why you would use `outerWidth` for anything like that. For example if you open the browser's developers tools and dock them to the right, then `outerWidth` and `innerWidth` start to diverge, giving bogus multiplier. There are probably more cases when this happens. – HankMoody Nov 27 '21 at 01:02
  • On my 4k LG screen using chromium based browser, window.devicePixelRatio doesn't show retina resolution but a fake one. That's why I use this ratio which seems to be accurate except eventually for the case you mention. – Fabien Auréjac Nov 27 '21 at 01:42
  • Here on macOS 12 with firefox and chromium based browsers, the case you mention is not a problem, that shows no bogus ratio. Can you tell me your configuration so that I could try to reproduce the bogus ratio you are writing about ? – Fabien Auréjac Nov 27 '21 at 01:52
  • Have you tried non maximized browser windows? On my side, when browser window is not maximized, then `outerWidth` is always 16px bigger than `innerWidth`. This is on Windows 10, Chrome. This is expected, as `outerWidth` is just window width and `innerWidth` is page width actually. – HankMoody Nov 27 '21 at 12:59
  • No I admit I did not try, though this doesn't affect much the scaling of the canvas, considering the ratio of 16 over window width is small. With this fix I'm able to detect at 1% near the real resolution and to fix the zoom poor or too rich resolutions. – Fabien Auréjac Nov 27 '21 at 18:57
  • excuse me, I understud maximized... I tried non maximized without problems under macOS and windows 11 preview emulated under Parallels. – Fabien Auréjac Nov 28 '21 at 01:54