如何强制画布大小以像素为单位的窗口的实际大小,而不考虑浏览器的缩放因子?

Posted

技术标签:

【中文标题】如何强制画布大小以像素为单位的窗口的实际大小,而不考虑浏览器的缩放因子?【英文标题】:how to force a canvas size to real size of a window in pixels, disregarding the zoom factor of the browser? 【发布时间】:2022-01-04 18:42:47 【问题描述】:

我正在这个网站上工作:sphere.mars2540.com。

javascript 或 css 中是否有任何方法可以强制画布大小不根据页面的缩放因子进行缩放,并始终对应于屏幕的实际像素(甚至 4k)? 我确实知道 devicePixelRatio,但它没有在我的 4k 屏幕上显示真实的比例

谢谢

【问题讨论】:

【参考方案1】:

我设法用 THREE.js 在 webgl 动画的上下文中找到了一个完整的答案: 使用这个技巧,我可以检测设备的缩放比例:

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();

然后我使用这个技巧检测到视网膜的变化:

var ratio2=window.devicePixelRatio/ratio1;

最后,我使用 THREE.js api 缩放画布大小,例如:

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

Todo -> 多田!它始终处于最大分辨率。

【讨论】:

我不知道你为什么要使用outerWidth 来做类似的事情。例如,如果您打开浏览器的开发人员工具并将它们停靠在右侧,那么outerWidthinnerWidth 就会开始发散,从而产生虚假的乘数。发生这种情况的情况可能更多。 在我使用基于铬的浏览器的 4k LG 屏幕上,window.devicePixelRatio 不显示视网膜分辨率,而是一个假的。这就是为什么我使用这个似乎是准确的比率,除了你提到的情况。 在 macOS 12 上使用基于 firefox 和 chromium 的浏览器,您提到的情况不是问题,这表明没有虚假比率。您能告诉我您的配置,以便我可以尝试重现您所写的虚假比率吗? 你试过非最大化的浏览器窗口吗?在我这边,当浏览器窗口未最大化时,outerWidth 总是比innerWidth 大 16px。这是在 Windows 10、Chrome 上。这是意料之中的,因为outerWidth 只是窗口宽度,innerWidth 实际上是页面宽度。 不,我承认我没有尝试过,虽然这对画布的缩放影响不大,考虑到 16 与窗口宽度的比例很小。通过这个修复,我能够检测到接近真实分辨率的 1%,并修复缩放不佳或太丰富的分辨率。【参考方案2】:

根据this answer,无法禁用页面缩放,这没关系。您可以做的,实际上更好的是调整画布大小以匹配当前页面缩放(或实际上使画布始终保持相同大小)。

这很简单,你只需要在你的画布调整大小计算中包含window.devicePixelRatio

当没有页面缩放时,window.devicePixelRatio 变量的值为1,并且每次缩放操作都会更改以匹配当前缩放百分比。

例如,要始终匹配窗口尺寸,您可以这样做:

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

要匹配父容器的大小,您可以这样做:

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

使用这些或类似的样式:

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

检测的最佳位置是需要调用上述函数并实际调整大小是使用requestAnimationFrame并将当前画布尺寸和devicePixelRatio值与上一帧的值进行比较(检查每一帧,但不要'如果不需要,请调整每个帧的大小)。

您也可以使用ResizeObserver API 来通过devicePixelContentBoxSize 属性获得更精确的尺寸,但目前浏览器支持非常有限。

【讨论】:

感谢 hankMoody 的提示,但在我的问题中,我还提到了我需要“视网膜”和高 dpi 显示器的解决方案,您知道是否可以检测到它?因为在 4k 屏幕上,我得到 0.6 的比率来进行 30% 的缩放。编辑:似乎 window.outerWidth/window.innerWidth 可以解决问题。 Retina 和高 DPI 显示器都由同一个变量表示,即。 devicePixelRatio。在此类屏幕上,devicePixelRatio 将大于 1 谢谢,正如我在评论编辑中所说,window.outerWidth/window.innerWidth 在我的屏幕上更准确,如果它适用于任何地方,我会进一步研究。

以上是关于如何强制画布大小以像素为单位的窗口的实际大小,而不考虑浏览器的缩放因子?的主要内容,如果未能解决你的问题,请参考以下文章

使用 VS6 C++ GUI 编辑器、MFC 以屏幕(像素)为单位调整全屏窗口大小?

更改文本画布大小

iOS:以像素为单位获取显示的图像大小

字体大小(以像素为单位)

pandas绘图设置背景

调整画布图像大小而不使其模糊