电脑基础 · 2023年3月14日

h5适配ios顶部和底部安全区域的问题

一. 前言:

苹果手机从iphoneX之后,屏幕顶部都有一个齐刘海,iPhoneX 取消了物理按键,改成底部小黑条,如果不做适配,这些地方就会被遮挡,所以本文记录一下齐刘海与底部小黑条的适配方法。

二. 知识点学习

1)viewport-fit
iOS11 新增特性,苹果公司为了适配 iPhoneX 对现有 viewport meta 标签的一个扩展,用于设置网页在可视窗口的布局方式,可设置三个值:

  • contain: 可视窗口完全包含网页内容
  • cover:网页内容完全覆盖可视窗口
  • auto:默认值,跟 contain 表现一致

注意:
网页默认不添加扩展的表现是 viewport-fit=contain,需要适配 iPhoneX 必须设置 viewport-fit=cover,这是适配的关键步骤。

2)env() 和 constant()
iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量(单位是px):如果是微信小程序的话,viewport-fit默认是 cover

  • safe-area-inset-left:安全区域距离左边边界的距离
  • safe-area-inset-right:安全区域距离右边边界的距离
  • safe-area-inset-top:安全区域距离顶部边界的距离
  • safe-area-inset-bottom:安全区域距离底部边界的距离

注意:
1)当 viewport-fit = contain 时 env() 是不起作用的,必须要配合 viewport-fit = cover 使用。对于不支持env() 的浏览器,浏览器将会忽略它。
需要做向后兼容。
2)env() 跟 constant() 需要同时存在,而且顺序不能换。

//适配底部小黑条
.page_bottom {
    padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS<11.2 */
    padding-bottom: env(safe-area-inset-bottom); /* 兼容iOS>= 11.2 */
 }

三. 适配步骤?

1)设置网页在可视窗口的布局方式
<meta name='viewport'  content="width=device-width, viewport-fit=cover" />
2)适配导航栏刘海屏

用的是vant的导航栏组件自带的 safe-area-inset-top 属性,因为van-nav-bar导航栏开启了fixed布局,导致页面下面的所有内容,直接上升到了页面的最顶部,部分内容会被van-nav-bar导航栏遮挡,我没用这个组件自带的placeholder占位,而是在外面又增加了一个class类名为navBarCon的div,给他一个和导航栏一样的高度,设置了 height:50px; 这样就会占位了

//适配导航栏刘海屏,用的是vant的导航栏组件,自带的 safe-area-inset-top 属性
<template>
  <div class="root">
    <div class="navBarCon">
      <van-nav-bar
        title="收货地址"
        left-arrow
        fixed
        safe-area-inset-top
        @click-left="navBack"
      />
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {}
  },
  mounted () {}
}
</script>
<style lang="scss" scoped>
::v-deep {
  .van-nav-bar .van-icon {
    color: #333 !important;
    font-weight: 600;
    font-size: 20px;
  }
  .van-nav-bar__title {
    color: #333;
    font-weight: 600;
  }
  .van-nav-bar__content {
    height: 50px;
  }
}
.root {
  height: 100vh;
  .navBarCon {
    height: 50px;
  }
}
</style>

对于安卓机这里会有个问题,会没有状态栏的一个区域,就是wifi,时间那一栏 (这是个困扰了一段时间的问题),今天解决了来记录下

大体思路:
判断手机系统是否是Andriod,是的话增加一个padding-top的样式作为安卓的头部距离;ios的话还是让他自己适配

// 判断手机系统
export const getNavigatorType = () => {
  let temp = ''
  var u = navigator.userAgent
  if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) {
    temp = 'Android'
  } else {
    temp = 'ios'
  }
  return temp
}
<script>
import { getNavigatorType } from '@/utils/tool.js'
 mounted () {
    let navigatorType = getNavigatorType()
    if (navigatorType === 'Android') {
    // 获取文档中的第一个class="van-nav-bar"的元素
      document.querySelector('.van-nav-bar').style.paddingTop = '20px'
    }
  },
</script>
3)适配底部小黑条 fixed 元素的适配

吸底的情况(bottom=0)

<template>
  <div class="root">
    <div class="page_bottom flexAround">
      <div class="amount">
        <span>合计:</span>
        <span class="red"></span>
        <span class="total_money red">{{ total_money }}</span>
      </div>
      <div class="payBtn" @click="confirmOrder">提交订单</div>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {}
  },
  mounted () {}
}
</script>
<style lang="scss" scoped>
/* 这个方案需要吸底条必须是有背景色的,因为扩展的部分背景是跟随外容器的,否则出现镂空情况。*/
.page_bottom {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: 45px; /*暂时发现安卓小屏手机安全区为0 && 不带单位的话padding-bottom会失效,写一个兜底, 并写在最上面;f12发现css样式如果都给height设置样式,会依次往下,用最底下的一个样式,如果最下面的没失效,依次往上查找,所以兜底的写在最上面 */
  height: calc(45px + constant(safe-area-inset-bottom)); /* 直接扩展高度,因为padding-bottom是内边距 */
  height: calc(45px + env(safe-area-inset-bottom)); /* 直接扩展高度 */
  background: #fff;
  padding: 0 11px 0 20px;
  box-sizing: border-box;
  padding-bottom: constant(safe-area-inset-bottom); /*兼容 iOS<11.2 */
  padding-bottom: env(safe-area-inset-bottom); /* 兼容iOS>= 11.2*/
}
.flexAround {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>

四:结语

为了方便维护使用,可以把顶部导航栏封装成一个子组件,在父组件中调用

推荐
fixed元素的适配 bottom=0和非0的情况
适配底部小黑条
yidongduanh5安全区适配
如何解决移动端的安全区域为0的问题