移动端视窗单位的小技巧

Avatar of Louis Hoebregts
Louis Hoebregts

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费信用额度!

视窗单位一直存在争议,部分原因是移动浏览器在实现它们时有自己的想法,从而使事情变得更加复杂。

例如:滚动条是否应计入vw单位?网站的导航或页面控件是否应计入计算?然后是设备本身的物理属性(您好,凹口!)也不能忽视。

首先,一些背景知识

关于如何计算视窗单位,规范非常含糊。对于移动设备,我们通常关注垂直高度,因此让我们专门看一下视窗高度(vh

vh 单位
等于初始包含块高度的 1%。

所以,在处理设备和浏览器特定的差异方面,没有明确的指导。

vh 最初是根据浏览器的当前视窗计算的。如果您打开浏览器并开始加载网站,则1vh 等于屏幕高度的 1%,减去浏览器界面。

但是!如果您开始滚动,情况就不同了。一旦您超过浏览器界面的一部分,例如地址栏,vh 值就会更新,结果是内容发生了尴尬的跳跃。

iOS 版 Safari 是第一个更新其实现的移动浏览器之一,它选择根据屏幕的最大高度定义vh 的固定值。这样,用户在地址栏消失后不会在页面上体验到跳跃。Chrome 的移动浏览器大约一年前也效仿了

截至撰写本文时,有一个票证来解决 Firefox Android 中的这个问题

虽然使用固定值很好,但也意味着如果地址栏可见,则无法拥有全高元素。元素的底部将被裁剪。

当地址栏可见时,元素在底部被裁剪(左),但我们想要的是完整的元素(右)。

CSS 自定义属性:正确大小调整的技巧

我突然想到,CSS 自定义属性和几行JavaScript 代码可能是获得一致且正确大小的完美方法。

在 JavaScript 中,您可以始终使用全局变量window.innerHeight 获取当前视窗的值。该值会考虑浏览器界面,并在其可见性发生变化时更新。诀窍是将视窗值存储在 CSS 变量中,并将该值应用到元素,而不是vh 单位。

假设我们的 CSS 自定义变量在本例中是--vh。这意味着我们将在 CSS 中这样应用它

.my-element {
  height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}

好的,我们已经设置好了。现在让我们在 JavaScript 中获取视窗的内部高度

// First we get the viewport height and we multiple it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);

我们告诉 JavaScript 获取视窗的高度,然后将其细化到总高度的 1/100,这样我们就有一个值可以作为视窗高度单位值分配。然后我们礼貌地要求 JS:root 中创建 CSS 变量(--vh)。

因此,我们现在可以像使用任何其他vh 单位一样使用--vh 作为我们的高度值,将其乘以 100,我们就会得到我们想要的全高。

最近出现了一种更常用的方法来解决这个问题。 Matt Smith 在这里对其进行了说明。诀窍是在主体上使用min-height: -webkit-fill-available; 作为100vh 的渐进增强,这应该在 iOS 设备上有效。

等等!还有一个细节。

虽然我们的工作可能看起来已经完成了,但是那些善于观察细节的人可能已经发现,JavaScript 会触发,但当视窗高度发生变化时,它永远不会更新元素的大小。继续调整上面的演示。

我们可以通过监听窗口resize 事件来更新--vh 的值。如果用户旋转设备屏幕(例如,从横向到纵向),或者导航在滚动时消失,这将非常有用。

// We listen to the resize event
window.addEventListener('resize', () => {
  // We execute the same script as before
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
});

⚠️ 更新--vh 的值将触发页面重新绘制,用户可能会因此体验到跳跃。因此,我不建议将此技巧用于每个项目或完全替代所有 vh 单位的使用,而只应在您可能需要用户拥有精确的视窗单位值时使用。

此外,您可能还想对 resize 事件实现一个防抖动方法,以避免在用户调整浏览器窗口大小时触发太多事件。您可以通过这篇文章了解更多信息:防抖动和节流通过示例解释

您现在可以调整上面的演示,并注意到 CSS 变量已相应更新。

虽然我最近在项目中使用了这种技术,并且它确实很有帮助,但在替换浏览器的默认行为时,您始终要三思而后行。(例如,这种情况在::focus 中很常见。)此外,浏览器现在往往更新非常快,因此请注意,今天的解决方案可能在明天不起作用。

同时,希望这篇文章对您有所帮助!👋

这里有一个提议,用于vhcvwc 单位,这可能是所有问题的救星。