相信很多小伙伴在開發(fā)微信小程序的時候都會有自定義頂部導航欄的需求,不夠要說明的是小程序右上角的膠囊是不能自定義的哦,除了膠囊其他地方都是可以根據自己的項目而定了,在一次小程序開發(fā)中就需要對頂部進行自定義在此記錄一下自己封裝這個組件的過程。
組件編寫
既然今天需要把導航欄封裝為組件那么就需要以下幾個步驟:
組件結構搭建
首先搭建一個如下圖的結構:

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

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

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

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

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

貌似這塊多高都可以,畢竟還可以向下延伸,經過多次測試(可能不準確)在蘋果好安卓還是會有差別的,所以自己弄了一個適配一部分的(貌似舊手機不支持沉沉侵式)??赡軓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>標題</view>
</view>
</view>
</view>
這里的主要思路就是用fixed定位,后面會有內容頂到底部的布局,所以全部用定位會方便些,整體就兩個部分一個是狀態(tài)欄一個就是標題和按鈕的部分了。
樣式
.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;
}
樣式中沒有太多的東西,唯一需要說的就是大部分模塊都是定位做的,唯一沒考慮好的就是標題太長不好處理,這里用了簡單粗暴的方法給設定一個寬度。
邏輯處理
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)信息時對于導航高度的判斷,另外一個是返回上一頁的邏輯,默認就是打開首頁(注意用的api),如果有自定義事件就用自定義事件customBackReturn
效果
目前效果就和沒有自定義之前差不多就是一個普通的返回按鈕和標題文字,如下圖:

那么這樣一個簡單的自定義導航欄就ok了,但是如果是這樣那我還自定義干嘛用小程序自帶的不是更好,所以我們要繼續(xù)改進,給加一個返回主頁的按鈕。
帶返回按鈕的導航
小程序中特別是分享出去的頁面,如果沒有一個返回主頁的按鈕,用戶更本不知道怎么回到小程序的主頁,所以給用戶帶來了很大的不便,因此這樣的需求就必須要有了,如果實現呢?其實很簡單,畢竟已經出來一個了,需要的就是加一個上去就行了。
結構
<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>標題</view>
</view>
</view>
</view>
大家注意到我把之前的代碼注釋了而不是直接在前面的代碼上修改,其實這里留著是為了后面可以自定義想要那種導航形式。
樣式
.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;
}
同樣的樣式也只是在之前的基礎上增加了上面這些樣式。
邏輯
homeClick() {
wx.switchTab({
url: '/pages/index/index',
})
}
邏輯也只是在methods中添加一個返回主頁的事件而已,這里需要大家根據自己的項目情況而定。
效果

樣式大家可以根據自己項目而定。
到這兩部分的功能都完成了,但是如果現在我們在首頁中添加一些內容你就會發(fā)現問題了,如下:

我們的內容也跑到上面了,不過這么一想內容頂到頂部不就是這種效果嗎,所以解決了這個問題,可現在的問題是不想要這種怎么辦呢?想想這個應該很好辦啊,我在這一部分給加一個等高的塊不定位不就可以了,所以現在就來實現它,代碼如下:
<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>標題</view>
</view>
</view>
</view>
效果如下:

當我們把false改為true是就可以得到內容頂到頂部的效果了,所以這樣就實現了兩種效果,既然我們這里是封裝為組件,那么我們不可能就這樣寫的,需要進行一些封裝,然后給其他頁面配置然后使用,所以需要改造。
封裝導航欄使其可定制化
為了能使使用更加方便,需要對上面的導航欄進行進一步封裝以適應不同需求。
結構改造
首先需要改造的就是結構,讓更多的選項能夠進行配置,結構改造如下:
<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>
上面所有的參數都是可以配置的,這就大大的給了開發(fā)者定制不同風格了,如果還是有不符合的建議你直接修改上面的代碼或者自己封裝一個更好。
邏輯改造
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
})
}
})
}
})
結構的改造其實就是添加一些能夠傳入的值來定制化。
到這里基本上這個組件就封裝好了,接下來就是使用這個組件了。
常規(guī)帶有一個返回按鈕的案例
這種方式的應用很簡單直接配置一些參數即可。
先引入組件如下:
{
"navigationStyle":"custom",
"usingComponents": {
"header":"../../components/navbar/navbar"
}
}
需要配置導航欄自定義,不然就沒有效果,根據自己的路徑引入即可,如果所有頁面都需要用到,建議直接在app.json中配置,這樣就不用每個頁面去配置了。
wxml中使用組件標簽
<header header='{{header}}'></header>
<view>內容區(qū)域哦</view>
在需要用到的頁面使用header標簽即可
js文件中配置參數
Page({
data: {
header:{
homeCapsule: false,
title: '標題',
fontColor: "#000",
fontSize: '36rpx',
headerbg: '#f40',
hiddenBlock: false,
slot: false
}
},
onLoad: function() {}
});
這里有三個參數是必須要配置的:homeCapsule,hiddenBlock,slot三個參數分別代表的含義是是否顯示帶有home按鈕的,是否隱藏整個頂部(后面會介紹),是否需要自定義結構
效果

那么這個最基本的就實現了。
內容置頂的實例
有時候需要將內容置頂,看起來會比較好看,所以就需要對hiddenBlock進行配置了,看代碼:
<header header='{{header}}'></header>
<image src='../../images/timg.jpg' style="width:100%" mode="aspectFill"></image>
<view>內容區(qū)域哦</view>
重要的是如何配置,如下:
Page({
data: {
header:{
homeCapsule: false,
title: '',
headerbg: 'transparent',
hiddenBlock: true,
slot: false
}
},
onLoad: function() {}
});
效果如下:

帶有home按鈕的導航
分享頁需要帶有返回首頁的按鈕,如何配置呢?有了這個組件就很容易配置了,配置如下:
Page({
data: {
header:{
homeCapsule: true,
title: '測試標題',
headerbg: '#f40',
hiddenBlock: false,
slot: false
}
},
onLoad: function() {}
});
效果如下:

完全自定義
如果以上都不能滿足需求,那么只有自己寫了,簡單實例如下:
<header header='{{header}}'>
<view>測試的標題哦</view>
</header>
<image src='../../images/timg.jpg' style="width:100%" mode="aspectFill"></image>
<view>內容區(qū)域哦</view>
配置如下:
Page({
data: {
header:{
headerbg: 'transparent',
hiddenBlock: false,
slot: true
}
},
onLoad: function() {}
});
效果如下:

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