最近在做小程序项目,需要用到自定义navigationBar,之前用过colorUI等组件库的navigationBar组件,但是现在想自己写一个,所以有了今天的小记

在做navigationBar之前我们先了解一下navigationBar是什么 ?

微信小程序一般来说有两个bar,一个导航栏,一个tabbar(小程序下方一排切换按钮),实现下方自定义tabbar的方法一般来说较为简单,现在着重叙述上方自定义导航栏的实现。

为什么要自定义navigationBar?

原生导航栏的限制

  • 除了胶囊按钮以外,原生导航栏只会出现返回按钮和当用户打开的小程序最底层页面是非首页时,默认展示的“返回首页”按钮 。
  • 原生导航栏的标题文字的颜色只有黑白。
  • 布局无法改变,不能做定制。

产品需求

  • 如果说原生导航栏的限制还不足以让你加入自定义导航栏,那么产品需求绝对是更大的推动力。

  • 自定义导航栏除了不能自定义胶囊按钮以外,其他的范围都是程序员的掌控范围,所以使用自定义导航栏无疑可以满足产品的各种需求。

 

 实际的开发过程中我们会用到以下方法:

  • wx.getSystemInfo()  用于获取系统信息(如设备品牌、设备型号、屏幕大小等,具体参数在文章末尾)
  • wx.getMenuButtonBoundingClientRect()  用于获取小程序的胶囊信息 

 了解了自定义NAVIGATIONBAR之后我们需要知道怎么做?

 

  1. 去掉原生导航栏。

  1. 将需要自定义navigationBar页面的page.json的navigationBarTitleText去掉。
  2. 加上"navigationStyle":"custom",这样原生的导航栏就已经消失,甚至后退键也不会出现,需要自定义。
  3. 另外,早在2016年微信已经开始适配沉浸式状态栏,目前几乎所有的机型里微信都是沉浸式状态栏,也就是说去掉原生导航栏的同时,整个屏幕已经成为可编程区域

 

2.计算navigationBar高度。

  • 原生的胶囊按钮当然存在,那么下一步就需要你去定位出自定义的导航栏高度以及位置。
  • 对于不同的机型,对于不同的系统,状态栏以及胶囊按钮的位置都不确定,所以需要用到一定的计算,从而面对任何机型都可以从容判定。
  1. 使用"wx.getSystemInfo()“获取到"statusBarHeight”,这样就确定了导航栏最基本的距离屏幕上方的距离。
  2. 使用"wx.getMenuButtonBoundingClientRect()"获取到小程序的胶囊信息(注意这个api存在各种问题,在不同端表现不一致,后面会叙述这个api调用失败的处理情况),如下图,以下坐标信息以屏幕左上角为原点。

 

3.以下图为例,上面的红色框是statusBar,高度已知;下面的红色框是正文内容,夹在中间的就是求解之一navigationBarHeight;而黄色的是原生胶囊按钮也是在垂直居中位置,高度为胶囊按钮基于左上角的坐标信息已知,不难得出,navigationBarHeight = 蓝色框高度 × 2 + 胶囊按钮.height。(蓝色框高度 = 胶囊按钮.top - statusBarHeight)

 

最后的计算公式为:navigationBarHeight = (胶囊按钮.top - statusBarHeight) × 2 + 胶囊按钮.height。navigationBar 距屏幕上方的距离即为navigationBarHeight。

这种计算方法在各种机型以及安卓ios都适用。

针对"wx.getMenuButtonBoundingClientRect()"获取错误或者获取数据为0的极少数情况,只能够去模拟,对于android,一般navigationBarHeight为48px,而对于ios一般为40px,所有机型的胶囊按钮高度是32px笔者也是通过网上很多的文章和自测得出的,这种误差一般可以忽略。当然最理想的就是微信可以hold住所有机型,呵呵。最后提醒一下仅以真机为准,开发者工具的bug就更多不说了。

 

代码实现 

  •  获取本机信息,笔者一般写在App的onLaunch中。

 app.js

//计算导航栏高度
    const { statusBarHeight, platform } = wx.getSystemInfoSync()
    const { top, height } = wx.getMenuButtonBoundingClientRect()

    // 状态栏高度
    wx.setStorageSync('statusBarHeight', statusBarHeight)
    // 胶囊按钮高度 一般是32 如果获取不到就使用32
    wx.setStorageSync('menuButtonHeight', height ? height : 32)
    
    // 判断胶囊按钮信息是否成功获取
    if (top && top !== 0 && height && height !== 0) {
//获取成功进行计算
        const navigationBarHeight = (top - statusBarHeight) * 2 + height
		// 导航栏高度
        wx.setStorageSync('navigationBarHeight', navigationBarHeight)
    } else {
//获取失败使用默认的高度
        wx.setStorageSync(
          'navigationBarHeight',
          platform === 'android' ? 48 : 40
        )
    }
  • 笔者将这几个高度信息储存在stroage中(也可以存到全局变量中),之后创建navigationBar自定义组件,在组件中将会运用到这些数据。

navigationBar.js

    // 状态栏高度
    statusBarHeight: wx.getStorageSync('statusBarHeight') + 'px',
    // 导航栏高度
    navigationBarHeight: wx.getStorageSync('navigationBarHeight') + 'px',
    // 胶囊按钮高度
    menuButtonHeight: wx.getStorageSync('menuButtonHeight') + 'px',
    // 导航栏和状态栏高度
    navigationBarAndStatusBarHeight: wx.getStorageSync('statusBarHeight') +
      wx.getStorageSync('navigationBarHeight') +
      'px',
    //标题
    title: '积分游戏'

 navigationBar.wxml

<view class="navigation-container" style="{{'height: ' + navigationBarAndStatusBarHeight}}">
    <!--空白来占位状态栏-->
    <view style="{{'height: ' + statusBarHeight}}"></view>
	<!--自定义导航栏-->
    <view class="navigation-bar" style="{{'height:' + navigationBarHeight}}">
        <!-- 这里就是导航栏 你可以将你想要的样式放到这里-->
    	<view class="navigation-buttons" style="{{'height:' + menuButtonHeight}}">
            <image class="nav-img" src='/images/back.svg'/>
            ...其余自定义button
        </view>

        <view class="navigation-title" style="{{'line-height:' + navigationBarHeight}}">{{title}}</view>
    </view>    
</view>
<!--空白占位fixed空出的位置-->
<view style="{{'height: ' + navigationBarAndStatusBarHeight}}; background: #ffffff"></view>

 navigationBar.wxss

/* navigationBar.wxss */
.navigation-container {
  position: fixed;
  width: 100%;
  z-index: 99;
  top: 0;
  left: 0;
  background-color: #ffffff;
}
.navigation-bar {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
}
.navigation-buttons {
  display: flex;
  align-items: center;
  margin-left: 20rpx;
  border: 1px solid rgba(0, 0, 0, 0.05);
  box-sizing: border-box;
  border-radius: 30rpx;
  background-color: transparent;
}
.nav-img{
  height: 32rpx;
  width: 32rpx;
}
.navigation-title {
  position: absolute;
  left: 208rpx;
  right: 208rpx;
  text-align: center;
  font-size: 32rpx;
  font-weight: bold;
  color: #000000;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

 

总结

  • 自定义导航栏核心在于导航栏的高度定位,这样才能准确定位自定义的返回按钮以及其他按钮的位置,与原生胶囊保持一致。至于wxml的布局方法多种多样,上面只是列出了笔者的一种写法。
  • 学习小程序,自定义导航栏是很重要的技能,其间的逻辑并不复杂,还是和学习前端一样,需要非常细心耐心,才能做好细节工作

 

wx.getSystemInfo()获取到的参数

 

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐