微信小程序自定义导航栏方案

1,166 阅读3分钟

最近都在写小程序,使用了自定义导航栏,自定义导航栏的好处是可以定制更多个性化的东西,但是入坑需谨慎

开启自定义导航栏

app.json设置

"window": {
  "navigationStyle": "custom"
}

思路

导航栏分为状态栏和标题栏

  • 状态栏很简单,微信提供了wx.getSystemInfo来获取设备信息,statusBarHeight即为状态栏的高度,我们来写一个状态栏

app.js:

App({
  onLaunch: function () {
    wx.getSystemInfo({
      success: res => {
        this.globalData.statusBarHeight = res.statusBarHeight
      }
    })
  },
  globalData: {
    statusBarHeight: '', //状态栏高度
  }
})

navbar.js:

const app = getApp()
Component({
  ...
  data: {
    statusBarHeight: app.globalData.statusBarHeight + 'px',
  }
  ...
})

navbar.wxml:

<view class="nav">
  <view class="status-bar" style="height: {{statusBarHeight}}"></view>
</view>
  • 接下来就是标题栏,实现之前先去搜索了一下别人是怎么实现,发现大部分文章给出的结论都是:

Android导航栏高度 48px

iOS导航栏高度 44px

一开始我也是把高度写死了,但是被我们ui给发现ios真机上中间的标题跟左侧的胶囊没有对齐: ),被发现了就得找原因

通过wx.getMenuButtonBoundingClientRect()可以获取到胶囊按钮的位置信息

App({
  onLaunch: function () {
    const capsuleStyle = wx.getMenuButtonBoundingClientRect()
    wx.getSystemInfo({
      success: res => {
        this.globalData.statusBarHeight = res.statusBarHeight
        this.globalData.capsuleTop = capsuleStyle.top - res.statusBarHeight
        this.globalData.capsuleRight = res.screenWidth - capsuleStyle.right
        this.globalData.capsuleWidth = capsuleStyle.width
        this.globalData.capsuleHeight = capsuleStyle.height
      }
    })
  },
  globalData: {
    statusBarHeight: '', //状态栏高度
    capsuleTop: '', //胶囊离状态栏距离 
    capsuleRight: '', //胶囊离右边屏幕的距离
    capsuleWidth: '', //胶囊的宽度
    capsuleHeight: '', //胶囊的高度
  }
})

经过实际测试发现真机和开发者工具模拟器上的胶囊按钮不一样

// ios 真机	
	capsuleTop 4px       //胶囊到状态栏的距离
	capsuleRight 7px			//胶囊离右侧屏幕的距离
	capsuleWidth 87px		//胶囊的宽度
	capsuleHeight 32px		//胶囊的高度
  
// Android 真机
	capsuleTop 8px
	capsuleRight 10px
	capsuleWidth 95px
	capsuleHeight 32px
    
// 开发者工具模拟器(iOS)
	capsuleTop 6px
	capsuleRight 10px
	capsuleWidth 87px
	capsuleHeight 32px
    
// 开发者工具模拟器(Android)
	capsuleTop 8px
	capsuleRight 10px
	capsuleWidth 87px
	capsuleHeight 32px

从上面可以看出, ios 真机上胶囊离状态栏的距离跟开发者工具上胶囊离状态栏的距离是不一样的,如果把 ios 导航栏高度写死

6 + 32 + 6 = 44px的话就会造成 ios 真机上右侧胶囊没在标题栏垂直居中,为了追求完美,在真机和开发者工具上胶囊都是垂直居中的,标题栏的高度应该写成动态的capsuleTop * 2 +statusBarHeight

还有一个问题: wx.getMenuButtonBoundingClientRect()这个接口目前存在BUG,得到的值偶尔会是错误的,所以在使用之前加了一个范围判断,不在这个范围的时候,就把值写死

App({
  onLaunch: function () {
    const capsuleStyle = wx.getMenuButtonBoundingClientRect()
    wx.getSystemInfo({
      success: res => {
        let isIOS = res.system.indexOf('iOS') > -1
        this.globalData.statusBarHeight = res.statusBarHeight
        this.globalData.capsuleTop = capsuleStyle.top - res.statusBarHeight
        if (this.globalData.capsuleTop < 1 || this.globalData.capsuleTop > 10) {
          this.globalData.capsuleTop = isIOS ? 4 : 8
        }
        this.globalData.capsuleRight = res.screenWidth - capsuleStyle.right
        if (this.globalData.capsuleRight < 5 || this.globalData.capsuleRight > 11) {
          this.globalData.capsuleRight = isIOS ? 7 : 10
        }
        this.globalData.capsuleWidth = capsuleStyle.width
        if (this.globalData.capsuleWidth < 80 || this.globalData.capsuleWidth > 100) {
          this.globalData.capsuleWidth = isIOS ? 87 : 95
        }
        this.globalData.capsuleHeight = capsuleStyle.height
        if (this.globalData.capsuleHeight < 32 || this.globalData.capsuleHeight > 32) {
          this.globalData.capsuleHeight = 32
        }
        //以上的范围只是自己写的大概
        this.globalData.titleBarHeight = this.globalData.capsuleTop * 2 + this.globalData.capsuleHeight
      }
    })
  },
  globalData: {
    statusBarHeight: '', //状态栏高度
    titleBarHeight: '', //标题栏高度
    capsuleTop: '', //胶囊离状态栏距离 
    capsuleRight: '', //胶囊离右边屏幕的距离
    capsuleWidth: '', //胶囊的宽度
    capsuleHeight: '', //胶囊的高度
  }
})

完整代码:

navbar.js:

const app = getApp()
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    navTitle: {
      type: String,
      value: '主页'
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    statusBarHeight: app.globalData.statusBarHeight + 'px',
    titleBarHeight: app.globalData.titleBarHeight + 'px',
    capsuleTop: app.globalData.capsuleTop + 'px', //胶囊离状态栏距离 
    capsuleRight: app.globalData.capsuleRight + 'px', //胶囊离右边屏幕的距离
    capsuleWidth: app.globalData.capsuleWidth + 'px', //胶囊的宽度
    capsuleHeight: app.globalData.capsuleHeight + 'px', //胶囊的高度
  },
  /**
   * 组件的方法列表
   */
  methods: {
    navHome() {

    },
    navBack() {

    }
  }
})

navbar.less:

.title-bar {
  position: relative;
  box-sizing: border-box;
  .left-capsule {
    border-radius: 16px;
    border: 1px solid #d8d8d8; //这里最好采用移动端1px方案
    box-sizing: border-box;
    position: relative;
    .iconfont {
      display: inline-flex;
      justify-content: center;
      align-items: center;
      font-size: 35rpx;
      width: 50%;
      height: 100%;
      background-color: #fff;
      &.icon-back {
        border-top-left-radius: 16px;
        border-bottom-left-radius: 16px;
      }
      &.icon-home {
        border-top-right-radius: 16px;
        border-bottom-right-radius: 16px;
      }
      &.nav-hover {
        opacity: 0.6;
        background: rgba(0, 0, 0, 0.1)!important;
      }
    }
    .line {
      position: absolute;
      background-color: #d8d8d8;
      width: 1px;
      height: 18px;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  }
  .title {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%)
  }
}

最后的结果