微信小程序自定義導(dǎo)航欄組件

相信很多小伙伴在開發(fā)微信小程序的時候都會有自定義頂部導(dǎo)航欄的需求,不夠要說明的是小程序右上角的膠囊是不能自定義的哦,除了膠囊其他地方都是可以根據(jù)自己的項目而定了,在一次小程序開發(fā)中就需要對頂部進行自定義在此記錄一下自己封裝這個組件的過程。

組件編寫

既然今天需要把導(dǎo)航欄封裝為組件那么就需要以下幾個步驟:

組件結(jié)構(gòu)搭建

首先搭建一個如下圖的結(jié)構(gòu):


1.png

然后在index中引入剛剛創(chuàng)建好的組件:


2.png

記得在json文件中添加引用路徑如圖一,此時就會看到首頁效果如下:
3.png

組件基礎(chǔ)代碼編寫

此時我們就需要根據(jù)官方文檔查看得知如果要定制必須在配置文件中修改默認(rèn)的配置文件,其實這里可以針對某個頁面page進行設(shè)置,以index為例在index.json文件中配置如下代碼即可看到如下效果:


4.png

這個時候我們就可以開始寫自己想要的樣子了,首先寫一個簡單的就只有一個返回按鈕,當(dāng)開始寫的時候就會發(fā)現(xiàn)一個很大的問題,這塊高度多少?最上面的狀態(tài)欄高度又是多少?貌似被這兩個問題難住了,這個時候再去看看官方的api文檔你會發(fā)現(xiàn)可以獲取系統(tǒng)當(dāng)前更多參數(shù)里面就有狀態(tài)欄高度,如圖:


5.png

其實想想我們只知道狀態(tài)欄高度,除了狀態(tài)欄的高度其余高度呢?又是一個問題。從微信小程序的官方設(shè)計文檔中可以知道這部分高度貌似一樣高,如圖:
6.png

貌似這塊多高都可以,畢竟還可以向下延伸,經(jīng)過多次測試(可能不準(zhǔn)確)在蘋果好安卓還是會有差別的,所以自己弄了一個適配一部分的(貌似舊手機不支持沉沉侵式)。可能廢話太多,直接上代碼吧

結(jié)構(gòu)
<view class='topbar'>
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <view class='navbar_back' bindtap='backClick'>
      <image src='../images/black_back.png'></image>
    </view>
    <view class='navbar_title' style="height:{{navHeight}}px">
      <view>標(biāo)題</view>
    </view>
  </view>
</view>

這里的主要思路就是用fixed定位,后面會有內(nèi)容頂?shù)降撞康牟季郑匀坑枚ㄎ粫奖阈w就兩個部分一個是狀態(tài)欄一個就是標(biāo)題和按鈕的部分了。

樣式
.topbar {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  z-index: 9999;
}
.status {
  width: 100%;
}
.navbar {
  width: 100%;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  position: relative;
}

.navbar_back {
  padding: 0 32rpx;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  height: 100%;
}

.navbar_back image {
  width: 21rpx;
  height: 34rpx;
}

.navbar_title {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: -1;
}
.navbar_title view {
  width: 40%;
  word-break: break-all;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 38rpx;
}

樣式中沒有太多的東西,唯一需要說的就是大部分模塊都是定位做的,唯一沒考慮好的就是標(biāo)題太長不好處理,這里用了簡單粗暴的方法給設(shè)定一個寬度。

邏輯處理
Component({
  properties: {
    /**
     * 自定義返回事件處理
     * customBackReturn="{{true}}" bind:customBackReturn="customBackReturn"
     */
    customBackReturn: {
      type: Boolean,
      value: false
    }
  },
  data: {

  },
  methods: {
    backClick() {
      if (this.data.customBackReturn) {
        this.triggerEvent("customBackReturn")
      } else {
        if (getCurrentPages().length == 1) {
          wx.switchTab({
            url: '/pages/index/index',
          })
        } else {
          wx.navigateBack({
            delta: 1
          })
        }
      }
    }
  },
  attached() {
    var self = this;
    wx.getSystemInfo({
      success(res) {
        var isIos = res.system.indexOf('iOS') > -1;
        self.setData({
          statusHeight: res.statusBarHeight,
          navHeight: isIos ? 44 : 48
        })
      }
    })
  }
})

邏輯中主要在兩個地方一個是獲取系統(tǒng)信息時對于導(dǎo)航高度的判斷,另外一個是返回上一頁的邏輯,默認(rèn)就是打開首頁(注意用的api),如果有自定義事件就用自定義事件customBackReturn

效果

目前效果就和沒有自定義之前差不多就是一個普通的返回按鈕和標(biāo)題文字,如下圖:


7.png

那么這樣一個簡單的自定義導(dǎo)航欄就ok了,但是如果是這樣那我還自定義干嘛用小程序自帶的不是更好,所以我們要繼續(xù)改進,給加一個返回主頁的按鈕。

帶返回按鈕的導(dǎo)航

小程序中特別是分享出去的頁面,如果沒有一個返回主頁的按鈕,用戶更本不知道怎么回到小程序的主頁,所以給用戶帶來了很大的不便,因此這樣的需求就必須要有了,如果實現(xiàn)呢?其實很簡單,畢竟已經(jīng)出來一個了,需要的就是加一個上去就行了。

結(jié)構(gòu)
<view class='topbar'>
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <view class='navbar_home'>
      <image src='../images/black_back.png' bindtap='backClick'></image>
      <image src='../images/home_black.png' bindtap='homeClick'></image>
    </view>
    <!-- <view class='navbar_back' bindtap='backClick'>
      <image src='../images/black_back.png'></image>
    </view> -->
    <view class='navbar_title' style="height:{{navHeight}}px">
      <view>標(biāo)題</view>
    </view>
  </view>
</view>

大家注意到我把之前的代碼注釋了而不是直接在前面的代碼上修改,其實這里留著是為了后面可以自定義想要那種導(dǎo)航形式。

樣式
.navbar_home {
  margin-left: 32rpx;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  border-radius: 33rpx;
  border: 1px solid rgba(0, 0, 0, 0.1);
  background: rgba(0,0,0,0.2);
  box-sizing: border-box;
  padding: 10rpx 0;
}

.navbar_home image:first-child {
  width: 21rpx;
  height: 34rpx;
  padding: 0 32rpx;
  border-right: 1px solid rgba(255,255,255,0.2);
}

.navbar_home image:last-child {
  width: 37rpx;
  height: 35rpx;
  padding: 0 32rpx;
}

同樣的樣式也只是在之前的基礎(chǔ)上增加了上面這些樣式。

邏輯
 homeClick() {
      wx.switchTab({
        url: '/pages/index/index',
      })
    }

邏輯也只是在methods中添加一個返回主頁的事件而已,這里需要大家根據(jù)自己的項目情況而定。

效果
8.png

樣式大家可以根據(jù)自己項目而定。

到這兩部分的功能都完成了,但是如果現(xiàn)在我們在首頁中添加一些內(nèi)容你就會發(fā)現(xiàn)問題了,如下:


9.png

我們的內(nèi)容也跑到上面了,不過這么一想內(nèi)容頂?shù)巾敳坎痪褪沁@種效果嗎,所以解決了這個問題,可現(xiàn)在的問題是不想要這種怎么辦呢?想想這個應(yīng)該很好辦啊,我在這一部分給加一個等高的塊不定位不就可以了,所以現(xiàn)在就來實現(xiàn)它,代碼如下:

<view style="height:{{statusHeight+navHeight}}px" hidden='{{false}}'></view>
<view class='topbar'>
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <view class='navbar_home'>
      <image src='../images/black_back.png' bindtap='backClick'></image>
      <image src='../images/home_black.png' bindtap='homeClick'></image>
    </view>
    <!-- <view class='navbar_back' bindtap='backClick'>
      <image src='../images/black_back.png'></image>
    </view> -->
    <view class='navbar_title' style="height:{{navHeight}}px">
      <view>標(biāo)題</view>
    </view>
  </view>
</view>

效果如下:


10.png

當(dāng)我們把false改為true是就可以得到內(nèi)容頂?shù)巾敳康男Ч耍赃@樣就實現(xiàn)了兩種效果,既然我們這里是封裝為組件,那么我們不可能就這樣寫的,需要進行一些封裝,然后給其他頁面配置然后使用,所以需要改造。

封裝導(dǎo)航欄使其可定制化

為了能使使用更加方便,需要對上面的導(dǎo)航欄進行進一步封裝以適應(yīng)不同需求。

結(jié)構(gòu)改造

首先需要改造的就是結(jié)構(gòu),讓更多的選項能夠進行配置,結(jié)構(gòu)改造如下:

<view style="height:{{statusHeight+navHeight}}px" hidden='{{header.hiddenBlock}}'></view>
<view class='topbar' style="background:{{header.headerbg}}">
  <view class='status' style="height:{{statusHeight}}px"></view>
  <view class='navbar' style="height:{{navHeight}}px">
    <block wx:if="{{header.slot}}">
      <slot></slot>
    </block>
    <block wx:else>
      <view class='navbar_home' wx:if="{{header.homeCapsule}}" style="background:{{header.capsulebg}};border:{{header.capsuleborder}}">
        <image src='../images/black_back.png' bindtap='backClick' style="border-right:{{header.capsulesep}}"></image>
        <image src='../images/home_black.png' bindtap='homeClick'></image>
      </view>
      <view class='navbar_back' bindtap='backClick' wx:else>
        <image src='../images/black_back.png'></image>
      </view>
      <view class='navbar_title' style="height:{{navHeight}}px">
        <view style="color:{{header.fontColor}};font-size:{{header.fontSize}}">{{header.title}}</view>
      </view>
    </block>
  </view>
</view>

上面所有的參數(shù)都是可以配置的,這就大大的給了開發(fā)者定制不同風(fēng)格了,如果還是有不符合的建議你直接修改上面的代碼或者自己封裝一個更好。

邏輯改造
Component({
  properties: {
    header: {
      type: Object,
      value: {
        homeCapsule: false,
        headerbg: "#fff",
        title: "",
        fontColor: "#000",
        fontSize: '16',
        hiddenBlock: false,
        capsulebg: 'rgba(0,0,0,0.2)',
        capsuleborder: '1px solid rgba(0, 0, 0, 0.1)',
        capsulesep: '1px solid rgba(255,255,255,0.2)',
        slot: false
      }
    },
    /**
     * 自定義返回事件處理
     * customBackReturn="{{true}}" bind:customBackReturn="customBackReturn"
     */
    customBackReturn: {
      type: Boolean,
      value: false
    }
  },
  methods: {
    backClick() {
      if (this.data.customBackReturn) {
        this.triggerEvent("customBackReturn")
      } else {
        if (getCurrentPages().length == 1) {
          wx.switchTab({
            url: '/pages/index/index',
          })
        } else {
          wx.navigateBack({
            delta: 1
          })
        }
      }
    },
    homeClick() {
      wx.switchTab({
        url: '/pages/index/index',
      })
    }
  },
  attached() {
    var self = this;
    wx.getSystemInfo({
      success(res) {
        var isIos = res.system.indexOf('iOS') > -1;
        self.setData({
          statusHeight: res.statusBarHeight,
          navHeight: isIos ? 44 : 48
        })
      }
    })
  }
})

結(jié)構(gòu)的改造其實就是添加一些能夠傳入的值來定制化。

到這里基本上這個組件就封裝好了,接下來就是使用這個組件了。

常規(guī)帶有一個返回按鈕的案例

這種方式的應(yīng)用很簡單直接配置一些參數(shù)即可。

先引入組件如下:
{
  "navigationStyle":"custom",
  "usingComponents": {
    "header":"../../components/navbar/navbar"
  }
}

需要配置導(dǎo)航欄自定義,不然就沒有效果,根據(jù)自己的路徑引入即可,如果所有頁面都需要用到,建議直接在app.json中配置,這樣就不用每個頁面去配置了。

wxml中使用組件標(biāo)簽
<header header='{{header}}'></header>
<view>內(nèi)容區(qū)域哦</view>

在需要用到的頁面使用header標(biāo)簽即可

js文件中配置參數(shù)
Page({
  data: {
    header:{
      homeCapsule: false,
      title: '標(biāo)題',
      fontColor: "#000",
      fontSize: '36rpx',
      headerbg: '#f40',
      hiddenBlock: false,
      slot: false
    }
  },
  onLoad: function() {}
});

這里有三個參數(shù)是必須要配置的:homeCapsule,hiddenBlock,slot三個參數(shù)分別代表的含義是是否顯示帶有home按鈕的,是否隱藏整個頂部(后面會介紹),是否需要自定義結(jié)構(gòu)

效果
11.png

那么這個最基本的就實現(xiàn)了。

內(nèi)容置頂?shù)膶嵗?/h4>

有時候需要將內(nèi)容置頂,看起來會比較好看,所以就需要對hiddenBlock進行配置了,看代碼:

<header header='{{header}}'></header>
<image src='../../images/timg.jpg' style="width:100%" mode="aspectFill"></image>
<view>內(nèi)容區(qū)域哦</view>

重要的是如何配置,如下:

Page({
  data: {
    header:{
      homeCapsule: false,
      title: '',
      headerbg: 'transparent',
      hiddenBlock: true,
      slot: false
    }
  },
  onLoad: function() {}
});

效果如下:


12.png

帶有home按鈕的導(dǎo)航

分享頁需要帶有返回首頁的按鈕,如何配置呢?有了這個組件就很容易配置了,配置如下:

Page({
  data: {
    header:{
      homeCapsule: true,
      title: '測試標(biāo)題',
      headerbg: '#f40',
      hiddenBlock: false,
      slot: false
    }
  },
  onLoad: function() {}
});

效果如下:


13.png

完全自定義

如果以上都不能滿足需求,那么只有自己寫了,簡單實例如下:

<header header='{{header}}'>
  <view>測試的標(biāo)題哦</view>
</header>
<image src='../../images/timg.jpg' style="width:100%" mode="aspectFill"></image>
<view>內(nèi)容區(qū)域哦</view>

配置如下:

Page({
  data: {
    header:{
      headerbg: 'transparent',
      hiddenBlock: false,
      slot: true
    }
  },
  onLoad: function() {}
});

效果如下:


14.png

當(dāng)然啦,這里只是簡單的舉個栗子而已,具體靠大家自己去實現(xiàn)了。
到此這組件大部分的封裝就完成了。謝謝大家哦!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容