這里使用三段論是什么為什么怎么做來闡述自定義navigationBar,如果只關(guān)心怎么做可直接跳至怎么做段落莺戒。
navigationBar是什么?
- 微信小程序一般來說有兩個bar害幅,一個導(dǎo)航欄赃绊,一個tabbar(小程序下方一排切換按鈕),實(shí)現(xiàn)下方自定義tabbar的方法一般來說較為簡單守问,現(xiàn)在著重敘述上方自定義導(dǎo)航欄的實(shí)現(xiàn)匀归。
小程序布局
- 談到導(dǎo)航欄與自定義導(dǎo)航欄,就需要解釋一下微信小程序的布局了耗帕。在小程序開發(fā)中使用wx.getSystemInfo() 方法可以獲取到系統(tǒng)信息穆端。
- 部分獲取到的信息如上圖(截取自微信小程序開發(fā)者文檔),對我們理解布局有用的信息是以上跟寬度高度相關(guān)的屬性仿便,如當(dāng)前設(shè)備的屏幕高寬体啰,可用高寬攒巍,以及saveArea。
- 上圖展示我們從systemInfo獲取到的數(shù)據(jù)的實(shí)際表現(xiàn)荒勇,以蘋果X的劉海屏為例(所有安卓劉海屏原理類似):最外層的紅色框即屏幕大小柒莉,藍(lán)色框即安全區(qū)域字面意思也就是開發(fā)者所能操縱的頁面區(qū)域,上面的黃色框即手機(jī)的狀態(tài)欄沽翔,綠色區(qū)域即我們要自定義的navigationBar兢孝。
- 可見,導(dǎo)航欄緊貼safeArea的上部仅偎,如果使用原生導(dǎo)航欄跨蟹,導(dǎo)航欄下方即是真正意義的可操控范圍。
- 實(shí)際上我們自定義的導(dǎo)航欄也是在這個safeArea內(nèi)與膠囊對齊最為和諧哨颂。很關(guān)鍵的原因就是微信將右上角的膠囊按鈕作為了內(nèi)置組件喷市,只有黑白兩種顏色相种,即我們無法改變它的大小位置透明度等等威恼,所以為了配合膠囊按鈕,一般自定義的導(dǎo)航欄位置也與上圖位置一致寝并。
為什么要自定義navigationBar箫措?
原生導(dǎo)航欄的限制
- 除了膠囊按鈕以外,原生導(dǎo)航欄只會出現(xiàn)返回按鈕和當(dāng)用戶打開的小程序最底層頁面是非首頁時衬潦,默認(rèn)展示的“返回首頁”按鈕 斤蔓。
- 原生導(dǎo)航欄的標(biāo)題文字的顏色只有黑白。
- 布局無法改變镀岛,不能做定制弦牡。
產(chǎn)品需求
如果說原生導(dǎo)航欄的限制還不足以讓你加入自定義導(dǎo)航欄,那么產(chǎn)品需求絕對是更大的推動力漂羊。
自定義導(dǎo)航欄除了不能自定義膠囊按鈕以外驾锰,其他的范圍都是程序員的掌控范圍,所以使用自定義導(dǎo)航欄無疑可以滿足產(chǎn)品的各種需求走越。
- 如果你的產(chǎn)品有上圖的需求椭豫,顯然你不能say no,你只能滿足需求旨指,加上自定義navigationBar赏酥。
自定義navigationBar怎么做?
去掉原生導(dǎo)航欄谆构。
- 將需要自定義navigationBar頁面的page.json的navigationBarTitleText去掉裸扶。
- 加上"navigationStyle":"custom",這樣原生的導(dǎo)航欄就已經(jīng)消失搬素,甚至后退鍵也不會出現(xiàn)需要自定義呵晨。
- 另外瞬项,早在2016年微信已經(jīng)開始適配沉浸式狀態(tài)欄,目前幾乎所有的機(jī)型里微信都是沉浸式狀態(tài)欄何荚,也就是說去掉原生導(dǎo)航欄的同時囱淋,整個屏幕已經(jīng)成為可編程區(qū)域。
計算navigationBarHeight餐塘。
- 原生的膠囊按鈕當(dāng)然存在妥衣,那么下一步就需要你去定位出自定義的導(dǎo)航欄高度以及位置。
- 對于不同的機(jī)型戒傻,對于不同的系統(tǒng)税手,狀態(tài)欄以及膠囊按鈕的位置都不確定,所以需要用到一定的計算需纳,從而面對任何機(jī)型都可以從容判定芦倒。
- 使用wx.getSystemInfo()獲取到statusBarHeight,這樣就確定了導(dǎo)航欄最基本的距離屏幕上方的據(jù)里不翩。
- 使用wx.getMenuButtonBoundingClientRect()獲取到小程序的膠囊信息(注意這個api存在各種問題兵扬,在不同端表現(xiàn)不一致,后面會敘述這個api調(diào)用失敗的處理情況)口蝠,如下圖器钟,以下坐標(biāo)信息以屏幕左上角為原點(diǎn)。
- 以下圖為例妙蔗,上面的紅色框是statusBar傲霸,高度已知;下面的紅色框是正文內(nèi)容眉反,夾在中間的就是求解之一navigationBarHeight昙啄;而黃色的是原生膠囊按鈕也是在垂直居中位置,高度為膠囊按鈕基于左上角的坐標(biāo)信息已知寸五,不難得出梳凛,navigationBarHeight = 藍(lán)色框高度 × 2 + 膠囊按鈕.height。(藍(lán)色框高度 = 膠囊按鈕.top - statusBarHeight)
最后的計算公式為:navigationBarHeight = (膠囊按鈕.top - statusBarHeight) × 2 + 膠囊按鈕.height播歼。navigationBar 距屏幕上方的距離即為navigationBarHeight伶跷。
這種計算方法在各種機(jī)型以及安卓ios都適用。
針對"wx.getMenuButtonBoundingClientRect()"獲取錯誤或者獲取數(shù)據(jù)為0的極少數(shù)情況秘狞,只能夠去模擬叭莫,對于android,一般navigationBarHeight為48px烁试,而對于ios一般為40px雇初,所有機(jī)型的膠囊按鈕高度是32px筆者也是通過網(wǎng)上很多的文章和自測得出的,這種誤差一般可以忽略减响。當(dāng)然最理想的就是微信可以hold住所有機(jī)型靖诗,呵呵郭怪。最后提醒一下僅以真機(jī)為準(zhǔn),開發(fā)者工具的bug就更多不說了刊橘。
代碼實(shí)現(xiàn)
- 獲取本機(jī)信息鄙才,筆者一般寫在App的onLaunch中。
App.js
// App.js
...
onLaunch(){
const { statusBarHeight, platform } = wx.getSystemInfoSync()
const { top, height } = wx.getMenuButtonBoundingClientRect()
// 狀態(tài)欄高度
wx.setStorageSync('statusBarHeight', statusBarHeight)
// 膠囊按鈕高度 一般是32 如果獲取不到就使用32
wx.setStorageSync('menuButtonHeight', height ? height : 32)
// 判斷膠囊按鈕信息是否成功獲取
if (top && top !== 0 && height && height !== 0) {
const navigationBarHeight = (top - statusBarHeight) * 2 + height
// 導(dǎo)航欄高度
wx.setStorageSync('navigationBarHeight', navigationBarHeight)
} else {
wx.setStorageSync(
'navigationBarHeight',
platform === 'android' ? 48 : 40
)
}
}
...
- 筆者將這幾個高度信息儲存在stroage中促绵,之后創(chuàng)建navigationBar自定義組件攒庵,在組件中將會運(yùn)用到這些數(shù)據(jù)。
navigationBar.js
// navigationBar.js
...
data: {
// 狀態(tài)欄高度
statusBarHeight: wx.getStorageSync('statusBarHeight') + 'px',
// 導(dǎo)航欄高度
navigationBarHeight: wx.getStorageSync('navigationBarHeight') + 'px',
// 膠囊按鈕高度
menuButtonHeight: wx.getStorageSync('menuButtonHeight') + 'px',
// 導(dǎo)航欄和狀態(tài)欄高度
navigationBarAndStatusBarHeight:
wx.getStorageSync('statusBarHeight') +
wx.getStorageSync('navigationBarHeight') +
'px'
}
...
- navigationBar.wxml中的布局就不多贅述败晴,一般來說浓冒,導(dǎo)航欄使用fixed定位,里面再通過行內(nèi)垂直居中的方式定位自定義的返回按鈕尖坤,還有居中導(dǎo)航標(biāo)題稳懒,以及字?jǐn)?shù)過多顯示省略號等。
navigationBar.wxml
<!--navigationBar.wxml-->
<view class="navigation-container" style="{{'height: ' + navigationBarAndStatusBarHeight}}">
<!--空白來占位狀態(tài)欄-->
<view style="{{'height: ' + statusBarHeight}}"></view>
<!--自定義導(dǎo)航欄-->
<view class="navigation-bar" style="{{'height:' + navigationBarHeight}}">
<view class="navigation-buttons" style="{{'height:' + menuButtonHeight}}">
<image class="nav-img" src='/images/back.svg'/>
...其余自定義button
</view>
<view class="navigation-title" style="{{'line-height:' + navigationBarHeight}}">{{title}}</view>
</view>
</view>
<!--空白占位fixed空出的位置-->
<view style="{{'height: ' + navigationBarAndStatusBarHeight}}; background: #ffffff"></view>
navigationBar.wxss
/* navigationBar.wxss */
.navigation-container {
position: fixed;
width: 100%;
z-index: 99;
top: 0;
left: 0;
background-color: #ffffff;
}
.navigation-bar {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
}
.navigation-buttons {
display: flex;
align-items: center;
margin-left: 10px;
border: 1px solid rgba(0, 0, 0, 0.05);
box-sizing: border-box;
border-radius: 15px;
background-color: transparent;
}
.nav-img{
height: 16px;
width: 16px;
}
.navigation-title {
position: absolute;
left: 104px;
right: 104px;
text-align: center;
font-size: 16px;
font-weight: bold;
color: #000000;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
總結(jié)
- 自定義導(dǎo)航欄核心在于導(dǎo)航欄的高度定位慢味,這樣才能準(zhǔn)確定位自定義的返回按鈕以及其他按鈕的位置场梆,與原生膠囊保持一致。至于wxml的布局方法多種多樣贮缕,上面只是列出了筆者的一種寫法辙谜。
- 學(xué)習(xí)小程序俺榆,自定義導(dǎo)航欄是很重要的技能感昼,其間的邏輯并不復(fù)雜,還是和學(xué)習(xí)前端一樣罐脊,需要非常細(xì)心耐心定嗓,才能做好細(xì)節(jié)工作。