前述
文末貼了自定義導(dǎo)航欄的源代碼,不想看分析的可以直接到最后提取。
自定義導(dǎo)航欄的核心問題
- 自定義導(dǎo)航欄的高度如何計算?
- 如何控制返回按鈕?
- 如何最小化影響頁面其他邏輯(解耦)?
- 如何做到配置化?
理想的組件使用方式
// 僅需要傳入導(dǎo)航欄的標(biāo)題
<navigation title="自定義導(dǎo)航欄"></navigation>
// 支持導(dǎo)航欄樣式配置化
<navigation title="自定義導(dǎo)航欄" titleColor="white" backgroundColor="none"></navigation>
導(dǎo)航欄高度的動態(tài)計算
自定義導(dǎo)航欄高度可以拆解成系統(tǒng)導(dǎo)航欄的高度 + 小程序?qū)Ш綑诘母叨龋渲邢到y(tǒng)導(dǎo)航欄高度可以通過 API
獲取,小程序?qū)Ш綑诟叨饶J(rèn)為 50px
// 根據(jù)不同機(jī)型動態(tài)計算導(dǎo)航欄理想高度
computeHeight() {
wx.getSystemInfo({
success: (res) => {
this.setData({
statusBarHeight: res.statusBarHeight,
navigationBarHeight: 50 + res.statusBarHeight,
});
},
});
}
返回按鈕的動態(tài)控制
核心邏輯:當(dāng)頁面堆棧超過1時,則顯示返回按鈕,否則不顯示。
ready() {
this.setData({ showBack: getCurrentPages().length > 1 });
}
返回事件攔截
有時候需要攔截返回來處理一些邏輯,比如彈出挽留彈窗等,則可以阻止默認(rèn)的返回事件
// 外部組件使用
<navigation title="攔截返回" noBack bindback="onBack"/>
// 組件內(nèi)部返回事件邏輯
back() {
if (getCurrentPages().length > 0 && !this.data.noBack) {
getCurrentPages().length > 1 && wx.navigateBack();
} else {
this.triggerEvent('back')
}
}
雙擊邏輯
在某些場景下,雙擊導(dǎo)航欄需要返回頂部或者進(jìn)行刷新
// 點(diǎn)擊導(dǎo)航欄,進(jìn)入雙擊邏輯判斷
onClick(e) {
if (this.data.clickNum === 0) {
this.setData({
clickNum: 1,
clickTimer: setTimeout(() => {
this.resetClick()
}, 300)
});
} else {
this.triggerEvent('doubleClick');
this.resetClick();
}
},
resetClick() {
clearTimeout(this.data.clickTimer)
this.setData({
clickNum: 0,
clickTimer: null
});
}
源代碼
navigation.json
{
"component": true
}
navigation.wxml,其中返回圖標(biāo)和首頁圖標(biāo)未提供
<!-- 導(dǎo)航欄占位符,高度與導(dǎo)航欄高度一致 -->
<view
class="placeholder"
wx:if="{{placeholder}}"
style="height: {{navigationBarHeight}}px;"/>
<!-- 導(dǎo)航欄 -->
<view
bindtap="onClick"
class="navigation-wrap"
style="color: {{titleColor}};height: {{navigationBarHeight}}px;line-height:{{navigationBarLineHeight}}px;background: {{backgroundColor}};"
>
<!-- 返回按鈕 -->
<view class="back-wrap" catchtap="back">
<image
wx:if="{{showBack}}"
src="/assets/images/navigation/navigation-back-{{titleColor == 'white' ? 'white' : 'black'}}.svg" />
<image
class="icon-home"
wx:if="{{!showBack && showHome}}"
src="/assets/images/navigation/navigation-home-{{titleColor == 'white' ? 'white' : 'black'}}.svg" />
</view>
<!-- 導(dǎo)航欄標(biāo)題 -->
<view class="title">{{title}}</view>
<!-- 導(dǎo)航欄右邊返回按鈕占位符,寬度與返回按鈕寬度一致 -->
<view class="back-wrap back-slot"></view>
</view>
navigation.js
Component({
properties: {
title: { /* 標(biāo)題 */ type: String, value: '' },
titleColor: { /* 標(biāo)題顏色 */ type: String, value: 'black' },
noBack: { /* 是否攔截返回 */ type: Boolean, value: false },
showHome: { /* 是否顯示首頁按鈕 */ type: Boolean, value: false },
backgroundColor: { /* 背景 */ type: String, value: '#ffffff' },
placeholder: { /* 是否需要占位 */ type: Boolean, value: true },
},
data: {
showBack: false, // 是否顯示返回按鈕
statusBarHeight: 0, // 頂部系統(tǒng)狀態(tài)條高度
navigationBarHeight: 0, // 導(dǎo)航欄高度
navigationBarLineHeight: 0, // 導(dǎo)航欄行高
navigationBarBackgroundHeight: 0, // 導(dǎo)航欄背景高度
clickNum: 0,
clickTimer: null,
},
ready() {
this.computeHeight();
this.setData({ showBack: getCurrentPages().length > 1 });
},
methods: {
// 點(diǎn)擊返回按鈕,默認(rèn)返回上一頁,如果返回被攔截則只上報一個back事件
back() {
if (getCurrentPages().length > 0 && !this.data.noBack) {
getCurrentPages().length > 1 && wx.navigateBack();
} else {
this.triggerEvent('back')
}
},
// 根據(jù)不同機(jī)型動態(tài)計算導(dǎo)航欄理想高度
computeHeight() {
wx.getSystemInfo({
success: (res) => {
this.setData({
statusBarHeight: res.statusBarHeight,
navigationBarHeight: 50 + res.statusBarHeight,
navigationBarLineHeight: 44 + res.statusBarHeight * 2,
});
this.triggerEvent('updateNavigationHeight', this.data.navigationBarHeight);
this.triggerEvent('updateStatusBarHeight', this.data.statusBarHeight);
},
});
},
// 點(diǎn)擊導(dǎo)航欄,進(jìn)入雙擊邏輯判斷
onClick(e) {
if (this.data.clickNum === 0) {
this.setData({
clickNum: 1,
clickTimer: setTimeout(() => {
this.resetClick()
}, 300)
});
} else {
this.triggerEvent('doubleClick');
this.resetClick();
}
},
resetClick() {
clearTimeout(this.data.clickTimer)
this.setData({
clickNum: 0,
clickTimer: null
});
},
},
});
navigation.wxss
<style lang="scss">
.placeholder {
width: 100%;
}
.navigation-wrap {
background: white;
text-align: center;
font-size: 36rpx;
overflow: hidden;
box-sizing: border-box;
width: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 1000;
display: flex;
.back-wrap {
text-align: left;
padding-left: 32rpx;
box-sizing: border-box;
width: 80rpx;
height: 100%;
image {
width: 16rpx;
height: 28rpx;
}
.icon-home {
width: 40rpx;
height: 36rpx;
}
}
.title {
flex: 1;
height: 100%;
padding:0 60px;
overflow:hidden;
display:-webkit-box;
word-break:break-all;
-webkit-line-clamp: 1;
text-overflow:ellipsis;
-webkit-box-orient: vertical;
}
}
</style>
組件使用案例
<!-- 基礎(chǔ)用法 -->
<navigation title="自定義導(dǎo)航欄"/>
<!-- 透明背景 + 不占任何空間 -->
<navigation title="自定義導(dǎo)航欄" backgroundColor="none" placeholder="{{false}}"/>
<!-- 黑夜模式:白色標(biāo)題 + 黑色背景 -->
<navigation title="自定義導(dǎo)航欄" titleColor="#FFFFFF" backgroundColor="#000000"/>
<!-- 攔截返回事件 -->
<navigation title="自定義導(dǎo)航欄" noback bindback="onBack"/>
<!-- 顯示首頁圖標(biāo) -->
<navigatoin title="自定義導(dǎo)航欄" showHome/>
<!-- 雙擊刷新頁面 -->
<navigation title="自定義導(dǎo)航欄" binddoubleClick="onRefresh"/>