Taro小程序自定義頂部導航欄

微信自帶的頂部導航欄是無法支持自定義icon和增加元素的,在開發小程序的時候自帶的根本滿足不了需求,分享一個封裝好的組件,支持自定義icon、擴展dom,適配安卓、ios、h5,全面屏。
我用的是京東的Taro多端編譯框架寫的小程序,原生的也可以適用,用到的微信/taro的api做調整就行,實現效果如下。
在這裏插入圖片描述
在這裏插入圖片描述


1、NavBar.js

import Taro from '@tarojs/taro';
import React, {
   
    Component } from 'react'
import {
   
    View } from '@tarojs/components';
import {
   
    isFunction } from '../../utils/index' //判斷是否爲函數,可以用loadsh的_isFunction,也可以自己封裝。
import './NavBar.less';


function getSystemInfo () {
   
   
  if (Taro.globalSystemInfo && !Taro.globalSystemInfo.ios) {
   
   
    return Taro.globalSystemInfo;
  } else {
   
   
    // h5環境下忽略navbar
    if (!isFunction(Taro.getSystemInfoSync)) {
   
   
      return null;
    }
    let systemInfo = Taro.getSystemInfoSync() || {
   
   
      model: '',
      system: ''
    };
    let ios = !!(systemInfo.system.toLowerCase().search('ios') + 1);
    let rect;
    try {
   
   
      rect = Taro.getMenuButtonBoundingClientRect ? Taro.getMenuButtonBoundingClientRect() : null;
      if (rect === null) {
   
   
        throw 'getMenuButtonBoundingClientRect error';
      }
      //取值爲0的情況  有可能width不爲0 top爲0的情況
      if (!rect.width || !rect.top || !rect.left || !rect.height) {
   
   
        throw 'getMenuButtonBoundingClientRect error';
      }
    } catch (error) {
   
   
      let gap; //膠囊按鈕上下間距 使導航內容居中
      let width = 96; //膠囊的寬度
      if (systemInfo.platform === 'android') {
   
   
        gap = 8;
        width = 96;
      } else if (systemInfo.platform === 'devtools') {
   
   
        if (ios) {
   
   
          gap = 5.5; //開發工具中ios手機
        } else {
   
   
          gap = 7.5; //開發工具中android和其他手機
        }
      } else {
   
   
        gap = 4;
        width = 88;
      }
      if (!systemInfo.statusBarHeight) {
   
   
        //開啓wifi的情況下修復statusBarHeight值獲取不到
        systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
      }
      rect = {
   
   
        //獲取不到膠囊信息就自定義重置一個
        bottom: systemInfo.statusBarHeight + gap + 32,
        height: 32,
        left: systemInfo.windowWidth - width - 10,
        right: systemInfo.windowWidth - 10,
        top: systemInfo.statusBarHeight + gap,
        width: width
      };
      console.log('error', error);
      console.log('rect', rect);
    }

    let navBarHeight = '';
    if (!systemInfo.statusBarHeight) {
   
   
      //開啓wifi和打電話下
      systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
      navBarHeight = (function () {
   
   
        let gap = rect.top - systemInfo.statusBarHeight;
        return 2 * gap + rect.height;
      })();

      systemInfo.statusBarHeight = 0;
      systemInfo.navBarExtendHeight = 0; //下方擴展4像素高度 防止下方邊距太小
    } else {
   
   
      navBarHeight = (function () {
   
   
        let gap = rect.top - systemInfo.statusBarHeight;
        return systemInfo.statusBarHeight + 2 * gap + rect.height;
      })();
      if (ios) {
   
   
        systemInfo.navBarExtendHeight = 4; //下方擴展4像素高度 防止下方邊距太小
      } else {
   
   
        systemInfo.navBarExtendHeight = 0;
      }
    }

    systemInfo.navBarHeight = navBarHeight; //導航欄高度不包括statusBarHeight
    systemInfo.capsulePosition = rect; //右上角膠囊按鈕信息bottom: 58 height: 32 left: 317 right: 404 top: 26 width: 87 目前發現在大多機型都是固定值 爲防止不一樣所以會使用動態值來計算nav元素大小
    systemInfo.ios = ios; //是否ios
    Taro.globalSystemInfo = systemInfo; //將信息保存到全局變量中,後邊再用就不用重新異步獲取了
    //console.log('systemInfo', systemInfo);
    return systemInfo;
  }
}
let globalSystemInfo = getSystemInfo();
class NavBar extends Component {
   
   
  constructor(props) {
   
   
    super(props);
    this.state = {
   
   
      configStyle: this.setStyle(globalSystemInfo)
    };
  }
  static options = {
   
   
    multipleSlots: true,
    addGlobalClass: true
  };

  UNSAFE_componentWillMount () {
   
   
    //獲取高度
    // let query = Taro.createSelectorQuery().in(this.$scope)
    // query.select('.lxy-nav-bar').boundingClientRect(rect=>{
   
   
    //   const navHeight = rect.height
    //   this.props.personalHomeMod.changeState('navHeight',navHeight)
    // 	// console.log('navHeight',toJS(this.props.personalHomeMod.state))
    // }).exec()
  }
  componentDidShow () {
   
   
    if (globalSystemInfo.ios) {
   
   
      globalSystemInfo = getSystemInfo();
      this.setState({
   
   
        configStyle: this.setStyle(globalSystemInfo)
      });
    }
  }
  handleBackClick () {
   
   
    if (isFunction(this.props.onBack)) {
   
   
      this.props.onBack();
    } else {
   
   
      const pages = Taro.getCurrentPages();
      if (pages.length >= 2) {
   
   
        Taro.navigateBack({
   
   
          delta: this.props.delta
        });
      }
    }
  }
  handleGoHomeClick () {
   
   
    if (isFunction(this.props.onHome)) {
   
   
      this.props.onHome();
    }
  }
  handleSearchClick () {
   
   
    if (isFunction(this.props.onSearch)) {
   
   
      this.props.onSearch();
    }
  }
  static defaultProps = {
   
   
    extClass: '',
    background: 'rgba(255,255,255,1)', //導航欄背景
    color: '#000000',
    title: '',
    searchText: '點我搜索',
    searchBar: false,
    back: false,
    home: false,
    iconTheme: 'black',
    delta: 1
  };

  setStyle (systemInfo) {
   
   
    const {
   
    statusBarHeight, navBarHeight, capsulePosition, navBarExtendHeight, ios, windowWidth } = systemInfo;
    const {
   
    back, home, title, color } = this.props;
    let rightDistance = windowWidth - capsulePosition.right; //膠囊按鈕右側到屏幕右側的邊距
    let leftWidth = windowWidth - capsulePosition.left; //膠囊按鈕左側到屏幕右側的邊距

    let navigationbarinnerStyle = [
      `color:${
     
     color}`,
      //`background:${background}`,
      `height:${
     
     navBarHeight + navBarExtendHeight}px`,
      `padding-top:${
     
     statusBarHeight}px`,
      `padding-right:${
     
     leftWidth}px`,
      `padding-bottom:${
     
     navBarExtendHeight}px`
    ].join(';');
    let navBarLeft;
    if ((back && !home) || (!back && home)) {
   
   
      navBarLeft = [
        `width:${
     
     capsulePosition.width}px`,
        `height:${
     
     capsulePosition.height}px`,
        `margin-left:0px`,
        `margin-right:${
     
     rightDistance}px`
      ].join(';');
    } else if ((back && home) || title) {
   
   
      navBarLeft = [
        `width:${
     
     capsulePosition.width}px`,
        `height:${
     
     capsulePosition.height}px`,
        `margin-left:${
     
     rightDistance}px`
      ].join(';');
    } else {
   
   
      navBarLeft = [`width:auto`, `margin-left:0px`].join(';');
    }
    return {
   
   
      navigationbarinnerStyle,
      navBarLeft,
      navBarHeight,
      capsulePosition,
      navBarExtendHeight,
      ios,
      rightDistance
    };
  }

  render () {
   
   
    const {
   
   
      navigationbarinnerStyle,
      navBarLeft,
      navBarHeight,
      capsulePosition,
      navBarExtendHeight,
      ios,
      rightDistance
    } = this.state.configStyle;
    const {
   
   
      title,
      background,
      backgroundColorTop,
      back,
      home,
      searchBar,
      searchText,
      iconTheme,
      extClass
    } = this.props;
    let nav_bar__center;
    if (title) {
   
   
      nav_bar__center = <text>{
   
   title}</text>;
    } else if (searchBar) {
   
   
      nav_bar__center = (
        <View
          className='lxy-nav-bar-search'
          style={
   
   `height:${
     
     capsulePosition.height}px;`}
          onClick={
   
   this.handleSearchClick.bind(this)}
        >
          <View className='lxy-nav-bar-search__icon' />
          <View className='lxy-nav-bar-search__input'>{
   
   searchText}</View>
        </View>
      );
    } else {
   
   
      /* eslint-disable */
      nav_bar__center = this.props.renderCenter;
      /* eslint-enable */
    }
    return (
      <View
        className={
   
   `lxy-nav-bar ${
     
     ios ? 'ios' : 'android'} ${
     
     extClass}`}
        style={
   
   `background: ${
     
     backgroundColorTop ? backgroundColorTop : background};height:${
     
     navBarHeight +
          navBarExtendHeight}px;`}
      >
        <View
          className={
   
   `lxy-nav-bar__placeholder ${
     
     ios ? 'ios' : 'android'}`}
          style={
   
   `padding-top: ${
     
     navBarHeight + navBarExtendHeight}px;`}
        />
        <View
          className={
   
   `lxy-nav-bar__inner ${
     
     ios ? 'ios' : 'android'}`}
          style={
   
   `background:${
     
     background};${
     
     navigationbarinnerStyle};`}
        >
          <View className='lxy-nav-bar__left' style={
   
   navBarLeft}>
            {
   
   back && !home && (
              <View
                onClick={
   
   this.handleBackClick.bind(this)}
                className={
   
   `lxy-nav-bar__button lxy-nav-bar__btn_goback ${
     
     iconTheme}`}
              />
            )}
            {
   
   !back && home && (
              <View
                onClick={
   
   this.handleGoHomeClick.bind(this)}
                className={
   
   `lxy-nav-bar__button lxy-nav-bar__btn_gohome ${
     
     iconTheme}`}
              />
            )}
            {
   
   back && home && (
              <View className={
   
   `lxy-nav-bar__buttons ${
     
     ios ? 'ios' : 'android'}`}>
                <View
                  onClick={
   
   this.handleBackClick.bind(this)}
                  className={
   
   `lxy-nav-bar__button lxy-nav-bar__btn_goback ${
     
     iconTheme}`}
                />
                <View
                  onClick={
   
   this.handleGoHomeClick.bind(this)}
                  className={
   
   `lxy-nav-bar__button lxy-nav-bar__btn_gohome ${
     
     iconTheme}}`}
                />
              </View>
            )}
            {
   
   !back && !home && this.props.renderLeft}
          </View>
          <View className='lxy-nav-bar__center' style={
   
   `padding-left: ${
     
     rightDistance}px`}>
            {
   
   nav_bar__center}
          </View>
          <View className='lxy-nav-bar__right' style={
   
   `margin-right: ${
     
     rightDistance}px`}>
            {
   
   this.props.renderRight}
          </View>
        </View>
      </View>
    );
  }
}

export default NavBar;

2、NavBar.less

view,
text,
scroll-view,
input,
button,
image,
cover-view {
   
   
  box-sizing: border-box;
}
page {
   
   
  /* prettier-ignore */
  --height: 44PX; /* 4*2+32 */
  /* prettier-ignore */
  --right: 97PX; /* 10+87 */
  /* prettier-ignore */
  --navBarExtendHeight: 4PX;
  /* prettier-ignore */
  --navBarHeight: 68PX;
  box-sizing: border-box;
}
.lxy-nav-bar .ios {
   
   
  /* prettier-ignore */
  --height: 44PX; /* 4*2+32 */
  /* prettier-ignore */
  --right: 97PX; /* 10+87 */
  /* prettier-ignore */
  --navBarExtendHeight: 4PX;
  box-sizing: border-box;
}
.lxy-nav-bar .android {
   
   
  /* prettier-ignore */
  --height: 48PX; /* 8*2+32 */
  /* prettier-ignore */
  --right: 96PX; /* 10+87 */
  /* prettier-ignore */
  --navBarExtendHeight: 4PX;
  box-sizing: border-box;
}
.lxy-nav-bar .devtools {
   
   
  /* prettier-ignore */
  --height: 42PX; /* 5*2+32 */
  /* prettier-ignore */
  --right: 88PX; /* 10+87 */
  /* prettier-ignore */
  --navBarExtendHeight: 4PX;
  box-sizing: border-box;
}
.lxy-nav-bar__inner {
   
   
  position: fixed;
  top: 0;
  left: 0;
  z-index: 5001;
  /* prettier-ignore */
  height:  var(--navBarHeight);
  display: flex;
  align-items: center;
  padding-right: var(--right);
  width: 100%;
  /* prettier-ignore */
  padding-top: 20PX;
  /* prettier-ignore */
  padding-bottom:4PX;
  .placeholder {
   
   
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
  }
}
.lxy-nav-bar__inner .lxy-nav-bar__left {
   
   
  position: relative;
  width: var(--right);
  /* prettier-ignore */
  height: 32PX;
  /*  padding-left: 10PX; */
  /* prettier-ignore */
  margin-left:10PX;
  display: flex;
  align-items: center;
}
.lxy-nav-bar__buttons {
   
   
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  /* prettier-ignore */
  border-radius: 16PX;
  border: 1px solid rgba(204, 204, 204, 0.6);
  position: relative;
}
.lxy-nav-bar__buttons.android {
   
   
  border: 1px solid rgba(234, 234, 234, 0.6);
}
.lxy-nav-bar__buttons::after {
   
   
  position: absolute;
  content: '';
  width: 1px;
  /* prettier-ignore */
  height: 18.4PX;
  background: rgba(204, 204, 204, 0.6);
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.lxy-nav-bar__buttons.android::after {
   
   
  background: rgba(234, 234, 234, 0.6);
}
.lxy-nav-bar__button {
   
   
  width: 50%;
  height: 100%;
  display: flex;
  /* prettier-ignore */
  font-size: 12PX;
  background-repeat: no-repeat;
  background-position: center center;
  background-size: 1em 2em;
}

.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback:active,
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome:active {
   
   
  opacity: 0.5;
}
.lxy-nav-bar__inner .lxy-nav-bar__center {
   
   
  /* prettier-ignore */
  font-size: 17PX;
  /* prettier-ignore */
  line-height: 17PX;
  text-align: center;
  position: relative;
  flex: 1;
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  align-items: center;
  justify-content: center;
  /* prettier-ignore */
  padding-left: 10PX;
  text {
   
   
    margin-top: -2px;
    font-size:34px;
    font-weight:550;
    line-height:44px;
  }
}
.lxy-nav-bar__inner .lxy-nav-bar__loading {
   
   
  font-size: 0;
}
.lxy-nav-bar__inner .lxy-nav-bar__loading .lxy-loading {
   
   
  margin-left: 0;
}
.lxy-nav-bar__inner .lxy-nav-bar__right {
   
   
  /* prettier-ignore */
  margin-right: 10PX;
}
.lxy-nav-bar__placeholder {
   
   
  height: var(--navBarHeight);
  background: #f8f8f8;
  position: relative;
  z-index: 50;
  visibility: hidden;
}

.lxy-nav-bar-search {
   
   
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  /* prettier-ignore */
  height: 32PX;
  /* prettier-ignore */
  border-radius: 16PX;
  position: relative;
  background: #f6f6f6;
}

.lxy-nav-bar-search__input {
   
   
  height: 100%;
  display: flex;
  align-items: center;
  color: #999;
  /* prettier-ignore */
  font-size: 15PX;
  /* prettier-ignore */
  line-height: 15PX;
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback {
   
   
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E  %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E");
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback.white {
   
   
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E  %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z' fill='%23ffffff'/%3E%3C/svg%3E");
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome {
   
   
  background-image: url("data:image/svg+xml,%3Csvg t='1565752242401' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4326' width='48' height='48'%3E%3Cpath d='M537.13744054-106.18008097999996c-12.56871953-12.56871953-31.42179957-12.56871953-50.27488108 0L28.10427953 195.46919964000006c-12.56871953 12.56871953-18.85308003 25.13744054-18.85308003 37.70616005l0 609.58292171c0 25.13744054 18.85308003 43.99052059 43.9905191 43.9905191l301.6492806 0c18.85308004 0 31.42179957-12.56871953 31.42180105-31.42179957l0-314.21800013c0-18.85308004 12.56871953-31.42179957 31.42179956-31.42180105l188.53080038 0c18.85308004 0 31.42179957 12.56871953 31.42179956 31.42180105l0 314.21800013c0 18.85308004 12.56871953 31.42179957 31.42180105 31.42179957L970.7582814 886.7488005c25.13744054 0 43.99052059-18.85308003 43.9905191-43.9905191L1014.7488005 233.17535969000005c0-12.56871953-6.2843605-25.13744054-18.85308003-37.70616005l-458.75827993-301.64928062z' fill='%23000000' p-id='4327'%3E%3C/path%3E%3C/svg%3E");
  /* prettier-ignore */
  background-size: 17PX 34PX;
  margin-top: 10px;
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome.white {
   
   
  background-image: url("data:image/svg+xml,%3Csvg t='1565752242401' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4326' width='48' height='48'%3E%3Cpath d='M537.13744054-106.18008097999996c-12.56871953-12.56871953-31.42179957-12.56871953-50.27488108 0L28.10427953 195.46919964000006c-12.56871953 12.56871953-18.85308003 25.13744054-18.85308003 37.70616005l0 609.58292171c0 25.13744054 18.85308003 43.99052059 43.9905191 43.9905191l301.6492806 0c18.85308004 0 31.42179957-12.56871953 31.42180105-31.42179957l0-314.21800013c0-18.85308004 12.56871953-31.42179957 31.42179956-31.42180105l188.53080038 0c18.85308004 0 31.42179957 12.56871953 31.42179956 31.42180105l0 314.21800013c0 18.85308004 12.56871953 31.42179957 31.42180105 31.42179957L970.7582814 886.7488005c25.13744054 0 43.99052059-18.85308003 43.9905191-43.9905191L1014.7488005 233.17535969000005c0-12.56871953-6.2843605-25.13744054-18.85308003-37.70616005l-458.75827993-301.64928062z' fill='%23ffffff' p-id='4327'%3E%3C/path%3E%3C/svg%3E");
  /* prettier-ignore */
  background-size: 17PX 34PX;
  margin-top: 10px;
}
.lxy-nav-bar-search__icon {
   
   
  /* prettier-ignore */
  width: 22PX;
  /* prettier-ignore */
  height: 22PX;
  display: flex;
  align-items: center;
  justify-content: center;
  background-image: url("data:image/svg+xml,%3Csvg t='1565691512239' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='1240' width='48' height='48'%3E%3Cpath d='M819.2 798.254545L674.909091 653.963636c46.545455-48.872727 74.472727-114.036364 74.472727-186.181818 0-151.272727-123.345455-274.618182-274.618182-274.618182-151.272727 0-274.618182 123.345455-274.618181 274.618182 0 151.272727 123.345455 274.618182 274.618181 274.618182 65.163636 0 128-23.272727 174.545455-62.836364l144.290909 144.290909c2.327273 2.327273 6.981818 4.654545 11.636364 4.654546s9.309091-2.327273 11.636363-4.654546c6.981818-6.981818 6.981818-18.618182 2.327273-25.6zM235.054545 467.781818c0-132.654545 107.054545-239.709091 239.709091-239.709091 132.654545 0 239.709091 107.054545 239.709091 239.709091 0 132.654545-107.054545 239.709091-239.709091 239.709091-132.654545 0-239.709091-107.054545-239.709091-239.709091z' fill='%23999999' p-id='1241'%3E%3C/path%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: cover;
}
input{
   
   
    padding:0 20px !important;
    text-align: start !important;
}

屬性:

在這裏插入圖片描述

slot:

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章