Bootstrap

微信小程序中安全区域计算和适配

前言

自从iphoneX问世之后,因为iphoneX、iphoneXR和后续全面屏手机设备,因为物理Home键被底部小黑条代替了,这时候很多前端小伙伴在开发的过程都会遇到 “全面屏”和“非全面屏”的兼容性问题,普遍问题就是底部按钮或者选项卡与底部黑线重叠

解释

根据官方解释:

安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)的影响。

具体区域如图展示

适配方案

当前有效的解决方式有几种

  1. 使用已知底部小黑条高度34px/68rpx来适配

  1. 使用苹果官方推出的css函数env()、constant()适配

  1. 使用微信官方API,getSystemInfo()中的safeArea对象进行适配

使用已知底部小黑条高度34px/68rpx来适配

这种方式是根据实践得出,通过物理方式测出iPhone底部的小黑条(Home Indicator)高度是34px,实际在开发者工具选中真机获取到高度也是34px,所以直接根据该值,设置margin-bottom、padding-bottom、height也能实现。同时这样做要有一个前提,需要判断当前机型是需要适配安全区域的机型。

但是这种方案相对来说是不推荐使用的。比较是一个比较古老原始的方案

使用苹果官方推出的css函数env()、constant()适配

这种方案是苹果官方推荐使用env(),constant()来适配,开发者不需要管数值具体是多少。

env和constant是IOS11新增特性,有4个预定义变量:

  • safe-area-inset-left:安全区域距离左边边界的距离

  • safe-area-inset-right:安全区域距离右边边界的距离

  • safe-area-inset-top:安全区域距离顶部边界的距离

  • safe-area-inset-bottom :安全距离底部边界的距离

具体用法如下:

Tips: constant和env不能调换位置
	padding-bottom: constant(safe-area-inset-bottom); /*兼容 IOS<11.2*/
	padding-bottom: env(safe-area-inset-bottom); /*兼容 IOS>11.2*/

其实利用这个能解决大部分的适配场景了,但是有时候开发需要自定义头部信息,这时候就没办法使用css来解决了

使用微信官方API,getSystemInfo()中的safeArea对象进行适配

通过 wx.getSystemInfo获取到各种安全区域信息,解析出具体的设备类型,通过设备类型做宽高自适应,话不多说,直接上代码

代码实现
const res = wx.getSystemInfoSync()
   const result = {
    ...res,
    bottomSafeHeight: 0,
        isIphoneX: false,
        isMi: false,
        isIphone: false,
        isIpad: false,
        isIOS: false,
        isHeightPhone: false,
  }
  const modelmes = result.model
    const system = result.system
    // 判断设备型号
    if (modelmes.search('iPhone X') != -1 || modelmes.search('iPhone 11') != -1) {
      result.isIphoneX = true;
    }
    if (modelmes.search('MI') != -1) {
      result.isMi = true;
    }
    if (modelmes.search('iPhone') != -1) {
      result.isIphone = true;
    }
    if (modelmes.search('iPad') > -1) {
      result.isIpad = true;
    }
    let screenWidth = result.screenWidth
    let screenHeight = result.screenHeight
    // 宽高比自适应
    screenWidth = Math.min(screenWidth, screenHeight)
    screenHeight = Math.max(screenWidth, screenHeight)
    const ipadDiff = Math.abs(screenHeight / screenWidth - 1.33333)
    if (ipadDiff < 0.01) {
      result.isIpad = true
    }
    if (result.isIphone || system.indexOf('iOS') > -1) {
      result.isIOS = true
    }
    const myCanvasWidth = (640 / 375) * result.screenWidth
    const myCanvasHeight = (1000 / 667) * result.screenHeight
    const scale = myCanvasWidth / myCanvasHeight
    if (scale < 0.64) {
      result.isHeightPhone = true
    }
    result.navHeight = result.statusBarHeight + 46
    result.pageWidth = result.windowWidth
    result.pageHeight = result.windowHeight - result.navHeight
    if (!result.isIOS) {
      result.bottomSafeHeight = 0
    }
   const capsuleInfo = wx.getMenuButtonBoundingClientRect()
    // 胶囊热区 = 胶囊和状态栏之间的留白 * 2 (保持胶囊和状态栏上下留白一致) * 2(设计上为了更好看) + 胶囊高度
    const navbarHeight = (capsuleInfo.top - result.statusBarHeight) * 4 + capsuleInfo.height
    // 写入胶囊数据
    result.capsuleInfo = capsuleInfo;
    // 安全区域
    const safeArea = result.safeArea
    // 可视区域高度 - 适配横竖屏场景
    const screenHeight = Math.max(result.screenHeight, result.screenWidth)
    const height = Math.max(safeArea.height, safeArea.width)
    // 状态栏高度
    const statusBarHeight = result.statusBarHeight
    // 获取底部安全区域高度(全面屏手机)
    if (safeArea && height && screenHeight) {
      result.bottomSafeHeight = screenHeight - height - statusBarHeight
      if (result.bottomSafeHeight < 0) {
        result.bottomSafeHeight = 0
      }
    }
    // 设置header高度
    result.headerHeight = statusBarHeight + navbarHeight
    // 导航栏高度
    result.navbarHeight = navbarHeight

;