移动端布局的坑:100vh在手机上为何不靠谱?

摘要:以后在手机上做全屏布局,别再死磕100vh了。用100svh做落地页,100dvh做应用界面。你的页面会稳如泰山,再也不会出现布局乱跳、内容被截的尴尬情况。代码简单,效果靠谱,用户看着舒服,自己也省心。

做前端开发的朋友应该都遇到过这种情况。写了一个全屏的页面模块,用了下面这行代码:

.hero {
  height: 100vh;
}

在电脑浏览器上看,一切正常。可一拿到手机上测试,问题就全冒出来了。页面布局乱跳,底部内容被截掉一半,滑动起来也不顺畅。很多人第一反应是怀疑自己代码写错了,反复检查flex布局、padding、margin,可问题依旧存在。

其实,这真不是你的技术问题。问题出在100vh这个单位上。在移动端浏览器里,100vh并不等于屏幕可视区域的高度。


为什么100vh在手机上会出问题?

手机浏览器和电脑浏览器不一样。手机屏幕上有一些固定元素:

  • 顶部的地址栏

  • 底部的导航栏

  • 各种工具栏

当你上下滑动页面时,这些元素会隐藏或显示。这就导致了一个问题:浏览器其实有两个高度。

一个是可视区域高度,就是你现在实际能看到的部分。另一个是完整高度,包括了工具栏背后隐藏的空间。

问题在于,100vh取的是完整高度,而不是可视高度。这就意味着,你设置height:100vh的元素,实际高度比用户看到的屏幕还要高出一截。底部自然就被切掉了一块。


三个真实场景,看100vh如何毁掉页面

场景一:首屏内容被截断

你做了一个落地页,大标题、副标题、按钮都在一个全屏模块里居中显示。在电脑上看着特别完美。可到了手机上,按钮不见了,或者只露出半个。用户想点按钮都点不到。

场景二:输入框弹出后布局错乱

登录页面设置了100vh。用户点输入框,手机键盘弹出来。视口高度瞬间变小,整个页面开始疯狂跳动,布局缩成一团,用户体验极差。

场景三:全屏弹窗不全屏

写了一个模态框,用fixed定位,高度设100vh。在iOS Safari上,模态框比屏幕大了一点,底部出现滚动条,关闭按钮跑到看不见的位置。


三个新的CSS单位解决问题

CSS新出了三个视口单位,专门解决这些问题。

svh:小视口高度

这个单位取的是最小可视高度。不管浏览器的工具栏怎么变化,它都使用那个不会被挡住的高度。适合用在首屏大图、落地页这些绝对不能出错的场景。

dvh:动态视口高度

这个单位会随着浏览器工具栏的显示隐藏实时变化。当地址栏出现或消失,它会自动调整高度。适合做全屏应用、聊天界面这些需要填满屏幕的布局。

lvh:大视口高度

这个取的是最大高度,包括工具栏背后的空间。这个单位用得很少,大多数场景都不适合。


实际开发中怎么用

首屏模块这样写

.hero {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: 20px;
}

用min-height而不是height,是为了防止内容太多时被截断。写两行是为了兼容老浏览器,不认识的会使用100vh,认识的用100svh覆盖。

全屏应用这样写

.app {
  height: 100dvh;
  overflow: auto;
}

工具栏显示隐藏时,应用高度会自动调整,始终填满屏幕。

弹窗这样写

.modal {
  position: fixed;
  inset: 0;
  height: 100dvh;
  overflow: auto;
}

这样弹窗用起来就像原生应用一样顺滑。


常见问题解答

是不是以后不能用100vh了?

不是。100vh在电脑上完全没问题,问题主要出在移动端浏览器。

三个新单位怎么选?

记住这个原则:落地页首屏用svh,追求稳定;应用类布局用dvh,追求实时适配;lvh基本用不上。

能不能全部用dvh?

不太建议。因为浏览器工具栏显示隐藏时,dvh会不断变化,导致页面频繁重绘。对于首屏来说,我们需要的是稳定,不是动态变化。


总结

以后在手机上做全屏布局,别再死磕100vh了。用100svh做落地页,100dvh做应用界面。你的页面会稳如泰山,再也不会出现布局乱跳、内容被截的尴尬情况。代码简单,效果靠谱,用户看着舒服,自己也省心。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_13330