小程序必备技巧(7)—封装自定义可扩展导航栏

551 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

假设现要封装一个自定义导航栏,最终呈现以下布局:

我们要从以下几个角度进行考虑:

  • 怎样才可以使得导航栏可以自定义
  • 如何预留出状态栏的位置,使得导航栏不被状态栏遮盖
  • 如何进行布局
  • 如何使封装的导航栏通用、可扩展

一、配置自定义导航栏

在配置自定义导航栏前,导航栏展示为:

此时,我们要在此页面的json文件中,配置"navigationStyle"选项为"custom"即可将导航变为可自定义的:

{
  "navigationStyle": "custom"
}
  • navigationStyle配置项仅支持配置为"default"、"custom"
  • default为默认样式
  • custom为自定义导航栏,只保留右上角的胶囊按钮

当我们配置上此选项后,展示为:

可见,只保留了右侧的胶囊按钮~那么接下来我们就可以根据需求来自定义导航栏啦!

当我们开始书写内容时,发现内容会从顶部左上角开始展示,会被状态栏遮盖住:

那么我们如何去预留出状态栏的位置让导航栏内容从状态栏下方开始展示呢?

二、预留出状态栏位置

我们知道,不同机型的状态栏高度是不一样的,拿iphone6/7/8与iphonex举例:

所以,我们要想正常展示出导航栏的内容,就要在导航栏的上方预留好状态栏的位置,而状态栏的高度需要获取不同机型的状态栏高度后动态设置上去即可~

2-1、布局出状态栏位置

首先,我们将封装的导航分为两大部分:状态栏与导航栏

<view class="nav">
<!-- 状态栏 -->
  <view class="status"></view>
  <!-- 导航栏 -->
  <view class="nav-bar"></view>
</view>

2-2、根据不同机型状态栏高度动态预留出状态栏位置

那么我们将如何获取不同机型的状态栏高度呢?

官方为我们提供了专门的api:wx.getSystemInfoAsync()

接口详情见官方文档:wx.getSystemInfoAsync(Object object)

  • 该api会返回包含设备相关信息的一个对象,如屏幕宽度、高度、状态栏高度、设备像素比等等
  • 其中statusBarHeight字段代表状态栏高度(number类型) 像这种比较通用的信息(可能其他页面也会用到)我们可以考虑在app.js中获取,放入globalData中方便共享:
// app.js
App({
  onLaunch: function() {
    const info = wx.getSystemInfoSync()
    this.globalData.statusBarHeight = info.statusBarHeight
  },
  globalData: {
    statusBarHeight: 0
  }
})

那么此时,在封装组件的js中,通过getApp().globalData.statusBarHeight即可拿到状态栏高度~

  /**
   * 组件的初始数据
   */
  data: {
    statusBarHeight: getApp().globalData.statusBarHeight
  },

我们获取到数据后,就可以根据状态栏高度为我们布局出来的状态栏的view设置啦:

此时我们再在导航栏view中去写内容时就会从该设备状态栏区域以下展示了:

三、根据需求进行导航栏布局

根据最终展示效果,导航栏大致分为左、中、右三个区域:

<view class="nav">
  <!-- 状态栏 -->
  <view class="status" style="height: {{statusBarHeight}}px"></view>
  <!-- 导航栏 -->
  <view class="nav-bar">
    <!-- 左边 -->
    <view class="left"></view>
    <!-- 中间 -->
    <view class="center"></view>
    <!-- 右边 -->
    <view class="right"></view>
  </view>
</view>

针对上述需求来说:

  • 对于左边与右边可给宽度:如120rpx
  • 右边一般展示胶囊按钮,不放置内容
  • 对于中间来说,可占取剩余位置
.nav-bar {
  display: flex;
  height: 44px;
  align-items: center;
  text-align: center;
}
.left, .right {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 120rpx;
}

.center {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
}

四、预留插槽使得导航栏通用、可扩展

针对不同页面,导航栏的展示内容会不同,可考虑预留出插槽让导航栏内容可自定义:

由于小程序中没有“默认插槽”的功能,我们可结合css在判断slot中内容为空时展示出默认内容即可:

.left-arrow {
  display: none;
}

.left-slot:empty + .left-arrow {
  display: block;
}

.left .left-arrow .icon {
  width: 44rpx;
  display: block;
}
.center-title {
  display: none;
}

.center-slot:empty + .center-title {
  display: block;
}

这样我们就可以实现插槽内容与默认内容的“互斥”效果啦!

(小程序实现“默认插槽”技巧可参考小程序必备技巧—实现“默认插槽”功能

要注意,这里有两个插槽,所以为了在插入内容时区分是向哪个插槽插入的,要使用具名插槽,为slot设置name属性!除此之外,要在组件的js的options选项中配置multipleSlots为true来开启多插槽功能:

Component({
  options: {
    multipleSlots: true
  },
......
})

至此,一个可扩展的nav-bar就封装完毕了~

五、使用封装的导航栏

我们此时可以在父组件中使用封装好的导航栏了

首先,在父组件的json中引入所需要使用的子组件:

然后使用:

<!-- 导航栏 -->
<nav-bar>
  <view slot="center" class="tab">
    <view class="active">歌曲</view>
    <view class="divider">|</view>
    <view>歌词</view>
  </view>
</nav-bar>

至此,大功告成~