为啥`1vh` CSS大小解析为屏幕上不同的像素大小

Posted

技术标签:

【中文标题】为啥`1vh` CSS大小解析为屏幕上不同的像素大小【英文标题】:Why `1vh` CSS size resolve to different pixel size on screen为什么`1vh` CSS大小解析为屏幕上不同的像素大小 【发布时间】:2020-06-22 06:55:00 【问题描述】:

我有三个div,高度相同,等于 1vh`。

我的问题是它们在屏幕上以不同的像素大小出现。有时它们看起来相等但有时不相等,特别是在调整视口大小后发生。请运行sn -p查看。

.samples 
  display:flex;


.container
  display:flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  margin:10px 20px;
  height: 20vh;
  width:20vh;


.container div
  background: #000;
  width:100%;


#set1 div
  height:.3vh;


#set2 div
  height:.7vh;


#set3 div
  height:.9vh;


#set4 div
  height:1.1vh;
<div class = "samples">

  <div class="container" id="set1" >
    <div></div>
    <div></div>
    <div></div>
  </div>

  <div class="container" id="set2" >
    <div></div>
    <div></div>
    <div></div>
  </div>

  <div class="container" id="set3" >
    <div></div>
    <div></div>
    <div></div>
  </div>
  
    <div class="container" id="set4" >
    <div></div>
    <div></div>
    <div></div>
  </div>
  
</div>
<div>
<h4>Above are four samples, each sample includes 3 identical div.</h4>
<h4>But depends on your screen size you may not see what expected</h4>
</div>

我截取了一些屏幕截图来演示调整大小期间实际发生的情况,您可以看到 div 看起来具有不同的高度,尽管它们都有 1vh 高度。

如果我在 javascript 中手动计算 1vh 并将其作为舍入的 'px' 注入到 CSS 中,那么它可以完美运行。(在 sn-p 中,我使用 .3、.7、.9、1.1 vh 进行演示)

  
  const set1Height = Math.round(document.documentElement.clientHeight * .3 / 100);
  const set2Height = Math.round(document.documentElement.clientHeight * .7 / 100);
  const set3Height = Math.round(document.documentElement.clientHeight * .9 / 100);
  const set4Height = Math.round(document.documentElement.clientHeight * 1.1 / 100);
  

  document.documentElement.style.setProperty("--set1-height", `$set1Heightpx`);
  document.documentElement.style.setProperty("--set2-height", `$set2Heightpx`);
  document.documentElement.style.setProperty("--set3-height", `$set3Heightpx`);
  document.documentElement.style.setProperty("--set4-height", `$set4Heightpx`);
  
.samples 
  display:flex;


.container
  display:flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  margin:10px 20px;
  height: 20vh;
  width:20vh;


.container div
  background: #000;
  width:100%;


#set1 div
  height: var(--set1-height);


#set2 div
  height: var(--set2-height);


#set3 div
  height: var(--set3-height);


#set4 div
  height: var(--set4-height);
<div class = "samples">

  <div class="container" id="set1" >
    <div></div>
    <div></div>
    <div></div>
  </div>

  <div class="container" id="set2" >
    <div></div>
    <div></div>
    <div></div>
  </div>

  <div class="container" id="set3" >
    <div></div>
    <div></div>
    <div></div>
  </div>
  
    <div class="container" id="set4" >
    <div></div>
    <div></div>
    <div></div>
  </div>
  
</div>
<div>
<h4>Here, after calculating heights and round them to `px` they looks identical</h4>
</div>

我的第一个想法是对浮点数进行四舍五入,但如果是这种情况,为什么 CSS 会将单个浮点数四舍五入为三个不同的数字?

我的问题是为什么会发生这种情况,是否有任何纯 CSS 解决方案可以安全地使用视口大小并确保它们始终按预期显示?

【问题讨论】:

好问题。在#set3 中,我尝试了flex: 0 1 .9vh; 而不是height: .9vh;,但它产生了相同的结果。 (flexflex-growflex-shrinkflex-basis 的简写属性。 感谢@Rounin 我也尝试不使用flex 并得到相同的结果。实际上,我使用了一些额外的 CSS,例如 flex 来显示发生了什么。 【参考方案1】:

我通过使用“px”而不是“vh”解决了这个问题

.container div
  background: #000;
  width:100%;
  height:6px;

由于 'vh' 是相对于视口的高度,因此在调整视口大小时可能会发生波动。

• 'px' 是一个绝对单位;但 'vh' 是一个相对单位。

【讨论】:

我认为以像素为单位添加 min-height 也可以解决问题,而且他仍然可以保留 vh 单位。 通过使用 'px' 而不是 'vh' 它会起作用,但它与屏幕大小无关。我可以使用 javascript 使其相对,我编辑了我的问题以显示它是如何可能的,但似乎不是很好的解决方案。 vh 与屏幕尺寸有关【参考方案2】:

对我来说,它看起来像下面这样。结果相差 1px。那个原因是因为 div 的计算高度不是整数,它是具有子像素值的浮点数。并且屏幕不能显示亚像素。因此,如果浏览器决定舍入到较低或较高的值,则取决于屏幕上的位置。

【讨论】:

谢谢@Brain Foo Long 在你的屏幕上它们现在看起来很好,但是如果你可以调整 sn-p 视口的大小,那么它们就会开始以不同的方式改变。我使用这些带有小数的样本只是为了显示发生了什么(但是我在你的屏幕上失败了!)。实际上,我使用1vh(不是浮点数)来构建汉堡菜单,然后有时取决于视口大小,菜单线的粗细彼此不同。【参考方案3】:

您在这里遇到的问题不在 CSS 层,而是在较低级别 - 在浏览器渲染引擎中,在某种程度上是物理显示硬件。

为什么 CSS 将一个浮点数四舍五入为三个不同的数字?

这是不对的 - CSS 行为正确,并且为每组中的所有三个 div 生成完全相同的高度值。您可以通过检查开发工具中的元素并查看每个 div 的计算高度属性来验证这一点。

但是,该值不是整个像素,而是一个像素的一小部分。您遇到了计算机图形学中的一个经典问题——如何在具有离散像素的显示器上显示像素的一部分?

简答——你不能,完美。这就是为什么当您调整它们的大小甚至在屏幕上移动它们时,这三个分数像素高度 div 最终会以 看起来 不同的高度呈现。渲染引擎尽最大努力使它们看起来相同,但结果却各不相同。

您在 JS 中的手动“四舍五入”解决方案有效的原因是,它会将这些高度四舍五入为整个像素值!当像素值是圆形时,一切都更容易完美排列(在幼稚的情况下,假设 div 之间没有小数像素填充、边距等)并且您不太可能看到差异。

试试吧!如果您删除 Math.round 或者只是简单地将 0.75 或其他内容添加到舍入结果中,您将看到与您的 JS 解决方案完全相同的问题,即使所有计算值显然相同。

同样,问题不在于值不同,而在于您的浏览器/硬件对它们的呈现方式不同。唯一普遍“安全”的解决方法是确保这些值实际上是圆形像素值。

引出这个问题:

是否有任何纯 CSS 解决方案可以安全地使用视口大小

就上述而言,得到四舍五入的像素值结果,不幸的是没有。 CSS calc() 或其他方式无法动态舍入结果。

作为一般建议,如果您需要精确渲染细线等像素,响应式 CSS 可能不是您想要使用的工具。您可能希望坚持使用静态像素值,但如果您真的需要动态的东西并随着视口大小的变化而增长和缩小,那么是的,您可能希望使用 JS 来更好地控制渲染。

【讨论】:

+1 表示问题的根源和清晰的解释。但只是关于这一点的一些精度:“您在 JS 中的手动“舍入”解决方案有效 [...] 当像素值是舍入时,不再存在渲染问题”。这并不总是正确的。提供的示例适用于我的全高清显示器,但即使 CSS 像素值是四舍五入的,我仍然在启用缩放的 4K 显示器上重现该问题。像素比也起作用 @blex 好喊,你当然是对的!它是“不太可能”而不是“否” - 已编辑以澄清细微差别。 @davnicwil 感谢您的明确解释。它涵盖了我问题的所有部分。【参考方案4】:

它很奇怪,但它总是对我有用 vw 也用于高度和宽度。并尝试最小高度

【讨论】:

以上是关于为啥`1vh` CSS大小解析为屏幕上不同的像素大小的主要内容,如果未能解决你的问题,请参考以下文章

uni-app 尺寸单位

微信小程序中的单位

关于pxremem物理像素等

元素 1 的 CSS 顶部为 x%,高度为 y%。元素 2 的 CSS 顶部为 (x+y)%。为啥像素值不同?

怎样区分图片的实际像素分辨率和尺寸大小

根据屏幕大小控制元素大小