微信小程序打夯之旅(九)- 自定义导航栏

前述

文末贴了自定义导航栏的源代码,不想看分析的能够直接到最后提取。javascript

自定义导航栏的核心问题

  • 自定义导航栏的高度如何计算?
  • 如何控制返回按钮?
  • 如何最小化影响页面其余逻辑(解耦)?
  • 如何作到配置化?

理想的组件使用方式

// 仅须要传入导航栏的标题
<navigation title="自定义导航栏"></navigation>

// 支持导航栏样式配置化
<navigation title="自定义导航栏" titleColor="white" backgroundColor="none"></navigation>
复制代码

导航栏高度的动态计算

自定义导航栏高度能够拆解成系统导航栏的高度 + 小程序导航栏的高度,其中系统导航栏高度能够经过 API 获取,小程序导航栏高度默认为 50pxcss

// 根据不一样机型动态计算导航栏理想高度
computeHeight() {
  wx.getSystemInfo({
    success: (res) => {
      this.setData({
        statusBarHeight: res.statusBarHeight,
        navigationBarHeight: 50 + res.statusBarHeight,
      });
    },
  });
}
复制代码

返回按钮的动态控制

核心逻辑:当页面堆栈超过1时,则显示返回按钮,不然不显示。html

ready() {
    this.setData({ showBack: getCurrentPages().length > 1 });
}
复制代码

返回事件拦截

有时候须要拦截返回来处理一些逻辑,好比弹出挽留弹窗等,则能够阻止默认的返回事件java

// 外部组件使用
<navigation title="拦截返回" noBack bindback="onBack"/>

// 组件内部返回事件逻辑
back() {
  if (getCurrentPages().length > 0 && !this.data.noBack) {
    getCurrentPages().length > 1 && wx.navigateBack();
  } else {
    this.triggerEvent('back')
  }
}
复制代码

双击逻辑

在某些场景下,双击导航栏须要返回顶部或者进行刷新web

// 点击导航栏,进入双击逻辑判断
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.jsonjson

{
  "component": true
}
复制代码

navigation.wxml,其中返回图标和首页图标未提供小程序

<!-- 导航栏占位符,高度与导航栏高度一致 -->
<view class="placeholder" wx:if="{{placeholder}}" style="height: {{navigationBarHeight}}px;"/>

<!-- 导航栏 -->
<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>

  <!-- 导航栏标题 -->
  <view class="title">{{title}}</view>

  <!-- 导航栏右边返回按钮占位符,宽度与返回按钮宽度一致 -->
  <view class="back-wrap back-slot"></view>
</view>
复制代码

navigation.jsbash

Component({
  properties: {
    title: { /* 标题 */ type: String, value: '' },
    titleColor: { /* 标题颜色 */  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,                   // 顶部系统状态条高度
    navigationBarHeight: 0,               // 导航栏高度
    navigationBarLineHeight: 0,           // 导航栏行高
    navigationBarBackgroundHeight: 0,     // 导航栏背景高度
    clickNum: 0,
    clickTimer: null,
  },
  ready() {
    this.computeHeight();
    this.setData({ showBack: getCurrentPages().length > 1 });
  },
  methods: {
    // 点击返回按钮,默认返回上一页,若是返回被拦截则只上报一个back事件
    back() {
      if (getCurrentPages().length > 0 && !this.data.noBack) {
        getCurrentPages().length > 1 && wx.navigateBack();
      } else {
        this.triggerEvent('back')
      }
    },

    // 根据不一样机型动态计算导航栏理想高度
    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);
        },
      });
    },

    // 点击导航栏,进入双击逻辑判断
    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.wxssmarkdown

<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>

复制代码

组件使用案例

<!-- 基础用法 -->
<navigation title="自定义导航栏"/>

<!-- 透明背景 + 不占任何空间 -->
<navigation title="自定义导航栏" backgroundColor="none" placeholder="{{false}}"/>

<!-- 黑夜模式:白色标题 + 黑色背景 -->
<navigation title="自定义导航栏" titleColor="#FFFFFF" backgroundColor="#000000"/>

<!-- 拦截返回事件 -->
<navigation title="自定义导航栏" noback bindback="onBack"/>

<!-- 显示首页图标 -->
<navigatoin title="自定义导航栏" showHome/>

<!-- 双击刷新页面 -->
<navigation title="自定义导航栏" binddoubleClick="onRefresh"/>
复制代码
相关文章
相关标签/搜索