微信小程序打夯之旅(九)- 自定義導(dǎo)航欄

前述

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

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