评论

小程序自定义导航栏完整适配方案

小程序自定义导航栏,使用uni-app,实现了微信和支付宝小程序通用导航栏的自定义方案。解决了ios系统微信小程序偶尔获取不到箭囊按钮信息带来的问题

写这篇博客的背景

临近节日,产品想给小程序首页头部设置图片背景,这个只能自定义导航栏来实现

当然除了自定义背景图,还可以放置其他组件,按钮、搜索框等

实践部分设备状态栏、胶囊、间距的高度(仅供参考)(单位px)

状态栏 胶囊 上下间距 整个导航栏高度
iPhone 5 20 32 4 60
iPhone 6/7/8 20 32 4 60
iPhone 6/7/8 Plus 20 32 4 60
iPhone X/XR/XS 44 32 4 84
小米6 (非刘海) 24 29 7 67
华为 nova 7 Pro(刘海) 42 29 7 85

后续遇到其他设备再补充

@[toc]

步骤

1.隐藏小程序自带的导航栏

小程序配置

// 1.全局配置
// app.json
{
	...
	"navigationStyle": "custom"
	...
}
// 2.页面配置
// pages.json
{
	...
	"navigationStyle": "custom"
	...
}

每一个小程序页面也可以使用 .json 文件来对本页面的窗口表现进行配置。页面中配置项在当前页面会覆盖 app.json 的 window 中相同的配置项。

2.编写自定义导航栏

  • 导航栏的组成部分(主要是状态栏和标题栏)
    1.状态栏(时间和电量显示那一栏)
    +
    2.状态栏和标题栏之间的间距
    +
    3.标题栏(小程序胶囊按钮那一栏)
    +
    4.标题栏和正文区域之间的间距

刘海屏

非刘海屏

  • 计算各部分的高度

获取系统信息api获取状态栏高度

// 状态栏高度
wx.getSystemInfoSync().statusBarHeight;

获取胶囊按钮信息api获取胶囊按钮的宽高和位置

/**
 * 获取微信小程序菜单栏(胶囊)信息
 * 菜单按键宽度:width
 * 菜单按键高度:height
 * 菜单按键上边界坐标:top
 * 菜单按键右边界坐标:right
 * 菜单按键下边界坐标:bottom
 * 菜单按键左边界坐标:left
 */
wx.getMenuButtonBoundingClientRect();

> 重点: 此api返回的是胶囊按钮在页面中的的上下左右坐标的绝对位置
> 注意:在模拟器使用时记得把视图百分比调为100%,否则可能会导致获取数据不准确

因为整个小程序的导航栏高度是不变的,我们可以把高度信息放在全局,方便使用。一般会在小程序的app.js(如果使用的uni-app,就是App.vue) 的 onLaunch生命周期进行获取和计算。

//app.js
App({
  onLaunch() {
    this.calcNavBarInfo()
  },
  
  globalData: {
    //全局数据管理
    navBarHeight: 0, // 导航栏高度
    menuBottom: 0, // 胶囊距底部间距(顶部间距也是这个)
    menuHeight: 0, // 胶囊高度
  },

  /**
   * @description 计算导航栏信息
   */
  calcNavBarInfo () {
    // 获取系统信息
    const systemInfo = wx.getSystemInfoSync();
    // 胶囊按钮位置信息
    const menuButtonInfo = wx.getMenuButtonBoundingClientRect();
    // 导航栏高度 = 状态栏到胶囊的间距(胶囊上坐标位置-状态栏高度) * 2 + 胶囊高度 + 状态栏高度
    this.globalData.navBarHeight = (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 + menuButtonInfo.height + systemInfo.statusBarHeight;
    // 状态栏和菜单按钮(标题栏)之间的间距
	// 等同于菜单按钮(标题栏)到正文之间的间距(胶囊上坐标位置-状态栏高度)
    this.globalData.menuBottom = menuButtonInfo.top - systemInfo.statusBarHeight;
    // 菜单按钮栏(标题栏)的高度
    this.globalData.menuHeight = menuButtonInfo.height;
  }
})
  • 到此各个部分的元素高度都已拿到, 而且是根据不同设备的屏幕信息动态设置,无论是刘海屏还是非刘海屏,安卓还是ios,样式皆可统一。

3.如何使用


<view class="nav" style="height:{{navBarHeight}}px; background: url();">
  <!-- 胶囊区域 -->
  <view class="capsule-box" style="height:{{menuHeight}}px; min-height:{{menuHeight}}px; line-height:{{menuHeight}}px; bottom:{{menuBottom}}px;">
    <view class="nav-handle">
      <view class="back">
      	<!-- 返回按钮 -->
      	<image src=""></image>
      </view>
      <view class="home">
      	<!-- 首页按钮 -->
      	<image src=""></image>
      </view>
    </view>
    <view class="nav-title">导航标题</view>
  </view>
</view>

// js
Page({
	data: {
		navBarHeight: getApp().globalData.navBarHeight,
    	menuBottom: getApp().globalData.menuBotton,
    	menuHeight: getApp().globalData.menuHeight 
	}
})


// style
// 导航栏
.nav {
	position: relative;
}
// 胶囊栏
.capsule-box {
	position: absolute;
	display: flex;
	align-items: center;
}
// 标题文字
.nav-title {
	height: 100%;
	width: 50%;
	margin: 0 auto; 
	overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

最近使用wx.getMenuButtonBoundingClientRect(),在ios端偶尔值全都是0,导致无法正确自定义导航栏高度

想了一下,ios端的所有设备胶囊按钮信息都是一样的,android端各个品牌都或多或少的有差距,根据这个规律作如下改造:
我这边后期用的uni-app ,所以api都换成了uni

uni.getSystemInfo({
        success: res => {
          let menuButtonInfo = {}
          if (res.platform === 'ios') {
            // ios设备的胶囊按钮都是固定的
            menuButtonInfo = {
              width: 87,
              height: 32,
              left: res.screenWidth - 7 - 87,
              right: res.screenWidth - 7,
              top: res.statusBarHeight + 4,
              bottom: res.statusBarHeight + 4 + 32
            }
          } else {
            // 安卓通过api获取
            menuButtonInfo = uni.getMenuButtonBoundingClientRect()
          }
          console.log('获取胶囊信息:', menuButtonInfo);
          // 导航栏高度 = 状态栏到胶囊的间距(胶囊距上未知-状态栏高度)* 2 + 胶囊高度 + 状态栏高度
          this.$options.globalData.navHeight = (menuButtonInfo.top - res.statusBarHeight) * 2 + menuButtonInfo.height + res.statusBarHeight;
          console.log('navHeight:', this.$options.globalData.navHeight);
          // 按钮上下边距高度
          this.$options.globalData.menuBottom = menuButtonInfo.top - res.statusBarHeight;
          // 导航栏右边到屏幕边缘的距离
          this.$options.globalData.menuRight = res.screenWidth - menuButtonInfo.right;
          // 导航栏高度
          this.$options.globalData.menuHeight = menuButtonInfo.height;
        },
        fail(err) {}
      })
最后一次编辑于  2022-03-04  
点赞 5
收藏
评论

5 个评论

  • Andre_安Jian
    Andre_安Jian
    2023-10-12

    请问后面改造的那个uni的是写在哪啊?有完整的改造代码吗?

    2023-10-12
    赞同
    回复 1
    • Charlie
      Charlie
      2023-10-17
      写在App.vue里的
      2023-10-17
      回复
  • 藏
    2023-06-13

    导航栏高度 = 状态栏到胶囊的间距(胶囊上坐标位置-状态栏高度) * 2 + 胶囊高度 + 状态栏高度

    这里为什么要乘以2呢?

    2023-06-13
    赞同
    回复 1
    • Charlie
      Charlie
      2023-06-28
      胶囊上下都有这个间距
      2023-06-28
      回复
  • 清风明月
    清风明月
    2022-12-11

    nav-title 标题的大小和颜色呢?

    2022-12-11
    赞同
    回复 1
    • Charlie
      Charlie
      2022-12-14
      这个可以自定义,自己写css
      2022-12-14
      回复
  • _wytぺ
    _wytぺ
    2022-11-25

    你好,能提供下nav-handle的样式吗

    2022-11-25
    赞同
    回复 1
    • Charlie
      Charlie
      2022-11-28
      这个就随便自定义,我现在找不到代码了
      2022-11-28
      回复
  • 哄哄
    哄哄
    2022-10-21

    好文章 感谢分享

    2022-10-21
    赞同
    回复 1
    • Charlie
      Charlie
      2022-10-21
      不客气
      2022-10-21
      回复
登录 后发表内容