自定义导航栏

学习小程序的过程中,发现原生导航栏的确不是特别美观,并且功能比较单一。
通过查看官方文档发现可以自定义导航栏,经过充分调研后,开始定义自己的导航组件。

经过测试,刘海屏也完美兼容,先来看一下最终效果。

普通屏幕:

navbar1.png

刘海屏:

navbar2.png

1. 全局缓存系统和右侧按钮信息

在app.js中缓存当前系统信息和菜单按钮位置信息。

getSystemInfo:获取系统信息,包括设备品牌、型号,状态栏高度等;
getMenuButtonBoundingClientRect:获取菜单按钮的布局位置信息;

App({
  onLaunch () {
    wx.getSystemInfo({
      success: (res) => {
        this.cache.systemInfo = res;
      }
    })
    this.cache.headerPos = wx.getMenuButtonBoundingClientRect();
  },
  cache: {
    systemInfo: {}, // 系统信息
    headerPos: {} // 按钮位置信息
  }
})

获取到相关位置信息,将信息缓存到cache对象中,以备后续使用。

2. components文件夹下建立navbar组件

目录结构如下:

navbar3.png

编写布局和样式文件

由于需要对不同设备进行兼容,不能使用默认高度,需要根据当前系统信息和菜单按钮信息进行计算,单位统一使用px。

导航栏分为两部分,一部分是导航栏,一部分是空白的占位盒子。
因为导航栏使用fixed定位,所有会脱离文档流,主体内容会上移,所以使用一个空白盒子进行占位,高度和导航栏一致。

导航栏分为文字部分和图标部分,高度和位置进行动态赋值。
文字设置居中显示,图标设置fiexed定位。

navbar.wxml

<view
  class="navbar"
  style="height: {{ navbarHeight }}px; padding-top: {{ statusBarHeight }}px;"
>
  <view
    wx:if="{{ showIcon }}"
    class="navbar-icon"
    style="top: {{ navbarPos.top + statusBarHeight }}px; left: {{ navbarPos.right }}px; height: {{ navbarPos.height }}px; width: {{ navbarPos.width }}px;"
  >
    <image 
      wx:if="{{ hasBack }}"
      class="left-arrow-img"
      src="/images/icon/left-arrow.png"
      catchtap="backBtnClick"
    />
    <image
      class="home-img"
      src="/images/icon/home.png"
      catchtap="homeBtnClick"
    />
  </view>
  <view
    class="navbar-title"
    style="line-height: {{ navbarPos.height + navbarPos.top }}px;"
  >
    <text>{{ title }}</text>
  </view>
</view>
<view
  style="height: {{ navbarHeight }}px; line-height: {{navbarHeight}}px;"
></view>

navbar.wxss

.navbar {
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;
  z-index: 999;
  box-sizing: border-box;
  background-color: rgba(255, 250, 250, .9);
}

.navbar .navbar-icon {
  position: fixed;
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  border-radius: 35rpx;
  border: .5rpx solid rgba(248, 248, 255, .9);
  box-sizing: border-box;
}

.navbar .navbar-icon .home-img,
.navbar .navbar-icon .left-arrow-img {
  width: 45rpx;
  height: 45rpx;
  padding-right: 5rpx;
}

.navbar .navbar-title {
  padding-top: 5rpx;
  text-align: center;
  font-weight: 600;
  font-size: 35rpx;
  color: #000;
}

3. navbar.js文件编写

左侧自定义图标及回调按钮位置,依靠状态栏高度和菜单按钮的位置信息计算。

状态栏高度:app.cache.systemInfo.statusBarHeight

自定义菜单按钮坐标:

1. 上边距:菜单按钮上边界坐标 - 状态栏高度
2. 右边距:屏幕宽度 - 菜单按钮右边界坐标
3. 下边距:菜单按钮下边界坐标 - 菜单按钮高度 - 状态栏高度
4. 导航栏高度:菜单按钮下边界坐标 + 下边距

注意点:

菜单按钮下边界坐标是包括状态栏、菜单按钮高度和菜单按钮高度之间的距离的,菜单按钮居中在导航栏中,所以上边距和下边距应该一致,所以菜单下边距应该是菜单按钮下边界坐标 - 菜单按钮高度 - 状态栏高度。

const app = getApp();

Component({
  properties: {
    title: String,
    showIcon: {
      type: Boolean,
      value: true
    }
  },

  data: {
    hasBack: true,
    statusBarHeight: 0,
    navbarHeight: 0,
    navbarPos: {
      height: 0,
      width: 0,
      top: 0,
      right: 0,
      bottom: 0,
      left: 0
    }
  },

  attached () {
    const { statusBarHeight, screenWidth } = app.cache.systemInfo,
          { headerPos } = app.cache;

    const navbarPos = {
      height: headerPos.height,
      width: headerPos.width,
      top: headerPos.top - statusBarHeight,
      bottom: headerPos.bottom - headerPos.height - statusBarHeight,
      right: screenWidth - headerPos.right
    }

    const hasBack = getCurrentPages().length === 1 ? false : true;

    this.setData({
      hasBack,
      statusBarHeight,
      navbarHeight: headerPos.bottom + navbarPos.bottom,
      navbarPos
    })
  },

  methods: {
    backBtnClick () {
      wx.navigateBack({
        delta: 1
      });
    },

    homeBtnClick () {
      wx.redirectTo({
        url: '/pages/index/index'
      });
    }
  }
})

主要逻辑在attched函数中,此函数是小程序自定义组件的生命周期钩子函数。将计算后的navbarPos赋值给data对象,供页面使用。

此外还使用getCurrentPages().length获取当前路由栈的长度,如果长度等于1,说明不是由路由跳转过来,就隐藏回退按钮的图标,默认都显示。

还可以配置showIcon来切换自定义图标的显示和隐藏。

4. 首页使用自定义导航栏组件

配置index.json文件

通过usingComponents引入navbar组件,配置navigationStyle为自定义(custom)。

{
  "usingComponents": {
    "common-header": "/components/header/common/common",
    "navbar": "/components/navbar/navbar"
  },
  "navigationStyle": "custom"
}

index.wxml 使用导航栏组件

在页面顶部使用navbar组件,传入参数title和showIcon。

<navbar 
  title="悦音乐"
  showIcon="{{ false }}"
/>

5. 总结

本次自定义组件样式比较简单,如果需要更加复杂的样式和效果,可以根据这个案例进行进一步的拓展,实现自己的业务需求。