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:

在这里插入图片描述

在这里插入图片描述

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
除了使用 `weixin-js-sdk`,在 Taro 微信小程序 webview 嵌套 Vue 3 + Vite 项目之间实现通信还有其他的方法,以下是一些常用的方法: 1. `window.postMessage()` 方法:可以通过 `window.postMessage()` 方法将消息发送到 Taro 微信小程序 webview 所在的 iframe 窗口。在 Taro 微信小程序中,可以通过监听 `message` 事件来接收从 Vue 3 + Vite 项目发送的消息。 2. WebSocket:可以使用 WebSocket 在 Taro 微信小程序 webview 和 Vue 3 + Vite 项目之间建立一个双向通信的连接。在 Taro 微信小程序中,可以使用 `wx.connectSocket()` 方法来建立 WebSocket 连接,并通过监听 `wx.onSocketMessage()` 方法来接收从 Vue 3 + Vite 项目发送的消息。 3. Storage API:可以使用 Storage API 在 Taro 微信小程序 webview 和 Vue 3 + Vite 项目之间共享数据。在 Taro 微信小程序 webview 和 Vue 3 + Vite 项目中都可以使用 `localStorage` 或 `sessionStorage` 对象来存储和读取数据。 4. 通过 URL 参数传递数据:可以通过在 URL 中添加参数的方式传递数据。在 Taro 微信小程序 webview 和 Vue 3 + Vite 项目中都可以通过 `window.location.href` 或 `wx.navigateTo()` 方法来跳转页面,并在 URL 中添加参数。 以上是一些常用的在 Taro 微信小程序 webview 嵌套 Vue 3 + Vite 项目之间实现通信的方法,根据实际需求可以选择不同的方法来实现。需要注意的是,在使用这些方法时要确保数据的安全性和正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

带风的少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值