起因与情况
昨天遇到了一个 Bug,有小伙伴反馈页面的应用窗口在手机上看不全,我自己拿 iPhone 16 试了一下,没复现,我就以为是一些杂牌浏览器的问题,以前也见过类似的,比如浏览器下方工具栏弹出的时候会遮挡页面。
在和他进行讨论后,发现他不管什么浏览器都会出现这种问题,包括换一台手机也一样,换的也是安卓,我也和他一起换,但我自己手机上还是没问题,后来我找了一台安卓机过来,复现了。
我发两张对比图:
白色主题的是 iOS 上的浏览器,黑色主题的是安卓上的浏览器,同样的代码,在不同设备上的效果不一样。
复现和分析
Demo 代码如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
.container {
position: absolute;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.3);
}
.card {
position: relative;
height: 90%;
width: 80%;
background: white;
}
.footer {
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 8px;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
卡片
</div>
</div>
<div class="footer">123123</div>
</body>
</html>
简述一下最小的复现场景,外层容器设置了position 和 100vw、100vh 的尺寸,内容高度是百分比,也就像相对于容器的。
按理说容器全屏,内容高度百分比,是绝对不会超出 viewport 的大小的,即便是浏览器上下工具栏对 viewport 有挤压,也只是会影响 viewport 的高度。
为了避免工具栏漂浮,覆盖了部分页面,我加了一个 footer 作为 fixed 固定在浏览器窗口下方,可以看到 123123 是没有被遮盖的,也就是说此时浏览器窗口并没有被下方工具栏漂浮遮挡。
所以我这里就觉得是 container 的问题了,后面排除了一下,确定问题出在 vw、vh 单位上,查了一下原因,也和读者分享一下。
vw、vh 单位,确实能表示 viewport 的尺寸,但是问题出在他们是静态的单位,他们在确定了具体的尺寸(px)之后就不会改变了,什么情况下会造成改变呢?比如工具栏,如果出现了工具栏,那么很明显用户能看到的部分变小了,但是 vh 并不会改变,可视区域小了,但尺寸不变,那肯定有一部分不可见了。
这一张来自 StackOverflow 的图就能很好的说明问题,viewport 高度始终是 100vh,但用户能看到的部分不等于 viewport。
还记得开头说的吗,iOS 上无法复现,对的,iOS 的 WebKit 和 安卓的 WebView 实现不一样,iOS 上用的是动态 vw、vh,也就是 viewport 的实际尺寸会根据可见区域进行调整,出现工具栏了,那么 100vh 对应的 px 就会减少,用户看到的可视区域就是 viewport。
解决
知道了原因,解决方案就出来了,有几种。
第一是自己维护一个变量作为 vw、vh 对应的像素数,通过 window 的 innerWidth 和 innerHeight 计算,再加上一个 resize 事件重算。
第二是用新单位 dvw 和 dvh,表示动态的 viewport 尺寸,22 年实装的,但肯定有不少用户懒得更新浏览器。
第三是避免这个单位,比如刚刚 Demo,完全可以用百分比来确定容器的尺寸。