移动端:安全区域和100vh问题

9 阅读4分钟

安全区域

segmentfault.com/a/119000002…

cloud.tencent.com/developer/a…

背景

  • 硬件设计变化:

    • 全面屏设计:手机取消了物理按键,采用了更大的显示区域
    • 刘海屏/打孔屏:顶部有摄像头和传感器的凹槽区域
    • 底部手势区域:取消物理Home键后,改用手势操作
    • 圆角屏幕:屏幕四角采用圆角设计
  • 安全区域的必要性:

    • 避免内容遮挡:防止页面重要内容被刘海、圆角、手势区域等遮挡
    • 保证可操作性:确保交互元素(如按钮)不会被系统手势区域干扰
    • 防止误触操作:底部按钮可能与系统手势冲突
    • 提升用户体验:让用户可以方便地进行系统操作(如返回手势、Home手势等)
    • 视觉体验:内容过于贴近边缘,影响美观
  • 安全区域的位置:

    在这里插入图片描述

    • 顶部:适配刘海屏/状态栏
    • 底部:适配手势操作区域(如底部工具栏、虚拟导航栏
    • 左右两侧:适配圆角和可能的手势区域

css变量

安全区域边界有4个预定义变量:主要用于处理移动设备底部安全区域的适配问题

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离
   padding-bottom: constant(safe-area-inset-bottom);
   padding-bottom: env(safe-area-inset-bottom);

具体来说:

  1. padding-bottom: constant(safe-area-inset-bottom); 和 padding-bottom: env(safe-area-inset-bottom); 这两行代码是用来处理全面屏手机(比如iPhone X及以后的机型)底部安全区域的内边距。
  2. 为什么需要这个:在全面屏手机上,底部会有一个手势区域或者虚拟Home键区域,这个区域不应该被应用的内容遮挡,否则会影响用户体验

使用了两个版本的语法:

  • constant() 是早期版本的语法
  • env() 是新版本的语法
  • 同时使用是为了保证更好的兼容性

使用场景

iPhoneX对比起以前其他的手机,屏幕顶部变成了留海屏,底部取消了物理按键改成了小黑条,这种改动导致了web开发中页面上新的适配问题。

比如一些需要贴在底部的按钮,和呼起的tabBar和底部弹出框,在iphoneX上就会出现被小黑条遮挡内容,或者页面上出现白色空隙的问题。

image.png image.png

 <head>
 <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover" name="viewport"/>
 </head>
 body {height: 100vh;}
 /* 你的贴底元素↓ */
 .container {
   position: absolute;
   bottom: 0;
   padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
   padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */
   ...
 }

vh问题

为什么设置安全区域还是没有成功

juejin.cn/post/719464…

正常而言:1vw 等于1/100的视口宽度 (Viewport Width),1vh 等于1/100的视口高度 (Viewport Height)

但是,在移动端,情况就不太一样了。100vh 不总是等于一屏幕的高度。有的时候,100vh 高度会出现滚动条。

根因在于:

  1. 很多浏览器,在计算 100vh 的高度的时候,会把地址栏等相关控件的高度计算在内
  2. 同时,很多时候,由于会弹出软键盘等操作,在弹出的过程中,100vh 的计算值并不会实时发生变化!

这也就变相导致了许多基于 100vh 想实现的效果无形中会产生很多问题。

解决

juejin.cn/post/740291…

 function setVh() {
   // Calculate 1vh in pixels
   const vh = window.innerHeight * 0.01;
   // Set the value in the --vh custom property to the root of the document
   document.documentElement.style.setProperty('--vh', `${vh}px`);
 }
 ​
 // Set the initial value
 setVh();
 ​
 // Recalculate on window resize
 window.addEventListener('resize', setVh);
 ​

JavaScript 代码

  • setVh 函数用于计算视口高度,并将其设置为 CSS 变量 --vh
  • window.innerHeight * 0.01 计算出 1vh 的像素值。
  • document.documentElement.style.setProperty('--vh', ${vh}px) 将计算出的值设置为 --vh 变量。
  • 页面加载时调用 setVh 函数,确保初始值正确。
  • 监听 resize 事件,在窗口大小改变时重新计算视口高度。
  .full-height {
    height: calc(var(--vh, 1vh) * 100);
    background-color: lightblue;
  }

CSS 样式

  • 我们在 :root 中定义了一个 CSS 变量 --vh,初始值为 100%
  • .full-height 类中,我们使用 calc(var(--vh, 1vh) * 100) 来设置高度,这样可以确保高度是动态计算的视口高度。