微信小程序自定义头部导航nav

Source

1.封装自定义nav导航组件

// app.js
App({
    
      
  globalData: {
    
      
    systeminfo: false,   //系统信息
    headerBtnPosi: false  //头部菜单高度
  }
})
// components/nav/nav.js
const app = getApp();
Component({
    
      
  properties: {
    
      
    vTitle: {
    
       // 标题
      type: String,
      value: ""
    },
    isSearch: {
    
      
      type: Boolean,
      value: false
    },
    noBorder: {
    
        // 不需要下边框
      type: Boolean,
      value: false
    },
    color: {
    
       // 标题颜色
      type: String,
      value: "black"
    },
    bg: {
    
       // 背景图片路径
      type: String,
      value: ""
    },
    showBack: {
    
        // 显示返回按钮
      type: Boolean,
      value: true
    }
  },
  data: {
    
      
    haveBack: true, // 是否有返回按钮
    statusBarHeight: 0, // 状态栏高度
    navbarHeight: 0, // 顶部导航栏高度
    navbarBtn: {
    
       // 胶囊位置信息
      height: 0,
      width: 0,
      top: 0,
      bottom: 0,
      right: 0
    },
    cusnavH: 0 //title高度
  },
  // 微信7.0.0支持wx.getMenuButtonBoundingClientRect()获得胶囊按钮高度
  attached: function () {
    
      
    if (!app.globalData.systeminfo) {
    
      
      app.globalData.systeminfo = wx.getSystemInfoSync()
    }
    if (!app.globalData.headerBtnPosi)
      app.globalData.headerBtnPosi = wx.getMenuButtonBoundingClientRect()
    let statusBarHeight = app.globalData.systeminfo.statusBarHeight // 状态栏高度
    let headerPosi = app.globalData.headerBtnPosi // 胶囊位置信息
    let btnPosi = {
    
      
      // 胶囊实际位置,坐标信息不是左上角原点
      height: headerPosi.height,
      width: headerPosi.width,
      top: headerPosi.top - statusBarHeight, // 胶囊top - 状态栏高度
      bottom: headerPosi.bottom - headerPosi.height - statusBarHeight, // 胶囊bottom - 胶囊height - 状态栏height (胶囊实际bottom 为距离导航栏底部的长度)
      right: app.globalData.systeminfo.windowWidth - headerPosi.right // 这里不能获取 屏幕宽度,PC端打开小程序会有BUG,要获取窗口高度 - 胶囊right
    }
    let haveBack
    if (this.properties.showBack === false) {
    
      
      haveBack = false
    } else {
    
      
      haveBack = true
    }
    
    var cusnavH = btnPosi.height + btnPosi.top + btnPosi.bottom // 导航高度
    this.setData({
    
      
      haveBack: haveBack, // 获取是否是通过分享进入的小程序
      statusBarHeight: statusBarHeight,
      navbarHeight: headerPosi.bottom + btnPosi.bottom, // 胶囊bottom + 胶囊实际bottom
      navbarBtn: btnPosi,
      cusnavH: cusnavH
    })
    //将实际nav高度传给父类页面
    this.triggerEvent("commonNavAttr", {
    
      
      height: headerPosi.bottom + btnPosi.bottom
    })
  },
  methods: {
    
      
    _goBack: function () {
    
      
      wx.navigateBack({
    
      
        delta: 1
      })
    },
    bindKeyInput: function (e) {
    
      
      // console.log(e.detail.value);
    }
  }
})
// components/nav/nav.json
{
    
      
  "component": true,
  "usingComponents": {
    
      },
  "navigationStyle": "custom"
}
<!-- components/nav/nav.wxml -->
<view class="custom_nav" style="height:{ 
          { 
          navbarHeight}}px;position: relative;">
  <image wx:if="{
     
       { !!bg }}" class="head-bg" mode="widthFix" src="{
     
       {bg}}"></image>
  <view class="custom_nav_box {
     
       {noBorder?'no-border':''}}" style="height:{ 
          { 
          navbarHeight}}px;">
    <view class="custom_nav_bar" style="top:{ 
          { 
          statusBarHeight}}px; height:{ 
          { 
          cusnavH}}px;">
      <block wx:if="{
     
       {isSearch}}">
        <input class="navSearch" style="height:{ 
          { 
          navbarBtn.height-2}}px;line-height:{ 
          { 
          navbarBtn.height-4}}px; top:{ 
          { 
          navbarBtn.top+1}}px; left:{ 
          { 
          navbarBtn.right}}px; border-radius:{ 
          { 
          navbarBtn.height/2}}px;" maxlength="10" bindinput="bindKeyInput" placeholder="输入文字搜索"/>
      </block>
      <block wx:else>
        <view class="custom_nav_icon" style="height:{ 
          { 
          navbarBtn.height}}px;line-height:{ 
          { 
          navbarBtn.height-2}}px; top:{ 
          { 
          navbarBtn.top}}px; left:{ 
          { 
          navbarBtn.right}}px;">
          <view wx:if="{
     
       {haveBack}}" class="icon-back" bindtap='_goBack'>
            <!-- <image src='/res/images/back.png' class='back-pre'></image> -->
            <iconfont name="back" color="#ff6200" size="44"/>
          </view>
        </view>
        <view class="nav_title" style="color: { 
          { 
          color}};height:{ 
          { 
          cusnavH}}px; line-height:{ 
          { 
          cusnavH}}px;">
          {
   
     {vTitle}}
        </view>
      </block>
    </view>
  </view>
</view>
/* components/nav/nav.wxss*/
.custom_nav {
    
      
  width: 100%;
  position: relative;
}

.head-bg {
    
      
  position: fixed;
  top: 0;
  left: 0;
  z-index: -1;
  width: 100%;
}
.custom_nav_box {
    
      
  position: fixed;
  width: 100%;
  z-index: 99999;
  border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
  box-shadow: 2rpx 2rpx 12rpx 0rpx rgba(95,95,95,0.1);
}
.custom_nav_box.no-border {
    
      
  border-bottom: none;
  box-shadow: none;
}

.custom_nav_bar {
    
      
  position: relative;
  z-index: 9;
}

.custom_nav_box .nav_title {
    
      
  font-size: 30rpx;
  text-align: center;
  position: absolute;
  max-width: 360rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  z-index: 1;
}

.custom_nav_box .custom_nav_icon {
    
      
  position:absolute;
  z-index: 2;
  display: inline-block;
  border-radius: 50%;
  vertical-align: top;
  font-size:0;
  box-sizing: border-box;
}
.custom_nav_box .custom_nav_icon.borderLine {
    
      
  border: 1rpx solid rgba(255, 255, 255, 0.3);
  background: rgba(0, 0, 0, 0.1);
}

.navbar-v-line {
    
      
  width: 1px;
  margin-top: 14rpx;
  height: 32rpx;
  background-color: rgba(255, 255, 255, 0.3);
  display: inline-block;
  vertical-align: top;
}

.icon-back {
    
      
  display: flex;
  align-items: center;
  width: 74rpx; 
  padding-left: 20rpx;
  height: 100%;
}

.icon-home {
    
      
  display: inline-block;
  width: 80rpx;
  text-align: center;
  vertical-align: top;
  height: 100%;
}

.icon-home .home_a {
    
      
  height: 100%;
  display: inline-block;
  vertical-align: top;
  width: 35rpx;
}

.custom_nav_box .back-pre,
.custom_nav_box .back-home {
    
      
  width: 40rpx;
  height: 40rpx;
}

.navSearch {
    
      
  width: 200px;
  background: #fff;
  font-size: 14px;
  position: absolute;
  padding: 0 20rpx;
  z-index: 9;
}

2.修改全局配置

navigationStyle 字段设置为 custom,这样就能使用自定义的nav。

// app.json
"window": {
    
      
  "backgroundTextStyle": "light",
  "navigationBarBackgroundColor": "#fff",
  "navigationBarTitleText": "测试",
  "navigationBarTextStyle": "black",
  "navigationStyle": "custom"
},

3.页面使用

需要使用自定义nav的页面,需要每个页面都要引入自定义组件:

// pages/service/service.json
{
    
      
  "usingComponents": {
    
      
    "nav-component": "/components/nav/nav"
  }
}
<!-- pages/service/service.wxml -->
<nav-component v-title="服务" showBack="{
     
       { false }}"></nav-component>

效果:
在这里插入图片描述
显示返回按钮,具体样式可以在自定义组件中定义默认样式:

<nav-component v-title="订单详情"></nav-component>

在这里插入图片描述
复杂一点的头部带背景图片:

<nav-component 
  v-title="首页" 
  color="white" 
  bg="/res/images/home_bg.png"
  no-border="{
     
       { true }}"
  showBack="{
     
       {false}}"
></nav-component>

在这里插入图片描述