微信小程序自定義navigationBar


這里使用三段論是什么為什么怎么做來闡述自定義navigationBar,如果只關心怎么做可直接跳至怎么做段落。

navigationBar是什么?

  • 微信小程序一般來說有兩個bar,一個導航欄,一個tabbar(小程序下方一排切換按鈕),實現下方自定義tabbar的方法一般來說較為簡單,現在着重敘述上方自定義導航欄的實現。

小程序布局

  • 談到導航欄與自定義導航欄,就需要解釋一下微信小程序的布局了。在小程序開發中使用wx.getSystemInfo() 方法可以獲取到系統信息。
 
wx.getSystemInfo()
 
image
  • 部分獲取到的信息如上圖(截取自微信小程序開發者文檔),對我們理解布局有用的信息是以上跟寬度高度相關的屬性,如當前設備的屏幕高寬,可用高寬,以及saveArea
 
image
  • 上圖展示我們從systemInfo獲取到的數據的實際表現,以蘋果X的劉海屏為例(所有安卓劉海屏原理類似):最外層的紅色框即屏幕大小,藍色框即安全區域字面意思也就是開發者所能操縱的頁面區域,上面的黃色框即手機的狀態欄,綠色區域即我們要自定義的navigationBar
  • 可見,導航欄緊貼safeArea的上部,如果使用原生導航欄,導航欄下方即是真正意義的可操控范圍。
  • 實際上我們自定義的導航欄也是在這個safeArea內與膠囊對齊最為和諧。很關鍵的原因就是微信將右上角的膠囊按鈕作為了內置組件,只有黑白兩種顏色,即我們無法改變它的大小位置透明度等等,所以為了配合膠囊按鈕,一般自定義的導航欄位置也與上圖位置一致。

為什么要自定義navigationBar?

原生導航欄的限制

  • 除了膠囊按鈕以外,原生導航欄只會出現返回按鈕和當用戶打開的小程序最底層頁面是非首頁時,默認展示的“返回首頁”按鈕 。
  • 原生導航欄的標題文字的顏色只有黑白。
  • 布局無法改變,不能做定制。

產品需求

  • 如果說原生導航欄的限制還不足以讓你加入自定義導航欄,那么產品需求絕對是更大的推動力。

  • 自定義導航欄除了不能自定義膠囊按鈕以外,其他的范圍都是程序員的掌控范圍,所以使用自定義導航欄無疑可以滿足產品的各種需求。

 
攜程navigationBar
 
斗魚navigationBar
  • 如果你的產品有上圖的需求,顯然你不能say no,你只能滿足需求,加上自定義navigationBar。

自定義navigationBar怎么做?

去掉原生導航欄。

  1. 將需要自定義navigationBar頁面的page.json的navigationBarTitleText去掉。
  2. 加上"navigationStyle":"custom",這樣原生的導航欄就已經消失,甚至后退鍵也不會出現需要自定義。
  3. 另外,早在2016年微信已經開始適配沉浸式狀態欄,目前幾乎所有的機型里微信都是沉浸式狀態欄,也就是說去掉原生導航欄的同時,整個屏幕已經成為可編程區域

計算navigationBarHeight。

  • 原生的膠囊按鈕當然存在,那么下一步就需要你去定位出自定義的導航欄高度以及位置。
  • 對於不同的機型,對於不同的系統,狀態欄以及膠囊按鈕的位置都不確定,所以需要用到一定的計算,從而面對任何機型都可以從容判定。
  1. 使用wx.getSystemInfo()獲取到statusBarHeight,這樣就確定了導航欄最基本的距離屏幕上方的據里。
  2. 使用wx.getMenuButtonBoundingClientRect()獲取到小程序的膠囊信息(注意這個api存在各種問題,在不同端表現不一致,后面會敘述這個api調用失敗的處理情況),如下圖,以下坐標信息以屏幕左上角為原點。
 
獲取結果
  1. 以下圖為例,上面的紅色框是statusBar,高度已知;下面的紅色框是正文內容,夾在中間的就是求解之一navigationBarHeight;而黃色的是原生膠囊按鈕也是在垂直居中位置,高度為膠囊按鈕基於左上角的坐標信息已知,不難得出,navigationBarHeight = 藍色框高度 × 2 + 膠囊按鈕.height。(藍色框高度 = 膠囊按鈕.top - statusBarHeight
 
image
  1. 最后的計算公式為:navigationBarHeight = (膠囊按鈕.top - statusBarHeight) × 2 + 膠囊按鈕.height。navigationBar 距屏幕上方的距離即為navigationBarHeight

  2. 這種計算方法在各種機型以及安卓ios都適用。

  3. 針對"wx.getMenuButtonBoundingClientRect()"獲取錯誤或者獲取數據為0的極少數情況,只能夠去模擬,對於android,一般navigationBarHeight為48px,而對於ios一般為40px,所有機型的膠囊按鈕高度是32px筆者也是通過網上很多的文章和自測得出的,這種誤差一般可以忽略。當然最理想的就是微信可以hold住所有機型,呵呵。最后提醒一下僅以真機為准,開發者工具的bug就更多不說了。

 
獲取失敗情況

代碼實現

  • 獲取本機信息,筆者一般寫在App的onLaunch中。

App.js

// App.js
...
onLaunch(){
    const { statusBarHeight, platform } = wx.getSystemInfoSync()
    const { top, height } = wx.getMenuButtonBoundingClientRect()

    // 狀態欄高度
    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
        // 導航欄高度
        wx.setStorageSync('navigationBarHeight', navigationBarHeight)
    } else {
        wx.setStorageSync(
          'navigationBarHeight',
          platform === 'android' ? 48 : 40
        )
    }
}
  • 筆者將這幾個高度信息儲存在stroage中,之后創建navigationBar自定義組件,在組件中將會運用到這些數據。

navigationBar.js

// navigationBar.js
...
data: {
    // 狀態欄高度
    statusBarHeight: wx.getStorageSync('statusBarHeight') + 'px',
    // 導航欄高度
    navigationBarHeight: wx.getStorageSync('navigationBarHeight') + 'px',
    // 膠囊按鈕高度
    menuButtonHeight: wx.getStorageSync('menuButtonHeight') + 'px',
    // 導航欄和狀態欄高度
    navigationBarAndStatusBarHeight:
      wx.getStorageSync('statusBarHeight') +
      wx.getStorageSync('navigationBarHeight') +
      'px'
}
  • navigationBar.wxml中的布局就不多贅述,一般來說,導航欄使用fixed定位,里面再通過行內垂直居中的方式定位自定義的返回按鈕,還有居中導航標題,以及字數過多顯示省略號等。

navigationBar.wxml

<!--navigationBar.wxml-->
<view class="navigation-container" style="{{'height: ' + navigationBarAndStatusBarHeight}}">
    <!--空白來占位狀態欄-->
    <view style="{{'height: ' + statusBarHeight}}"></view>
    <!--自定義導航欄-->
    <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;
}

總結

  • 自定義導航欄核心在於導航欄的高度定位,這樣才能准確定位自定義的返回按鈕以及其他按鈕的位置,與原生膠囊保持一致。至於wxml的布局方法多種多樣,上面只是列出了筆者的一種寫法。
  • 學習小程序,自定義導航欄是很重要的技能,其間的邏輯並不復雜,還是和學習前端一樣,需要非常細心耐心,才能做好細節工作。

轉載-鏈接:https://www.jianshu.com/p/9822d9ee168e
來源:簡書


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM