-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 Answers 2

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.

6
  • 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, 2021 at 1: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. Nov 27, 2021 at 1: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 ? Nov 27, 2021 at 1: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, 2021 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. Nov 27, 2021 at 18:57
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.

3
  • 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. Nov 26, 2021 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, 2021 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. Nov 26, 2021 at 21:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.