微信自带的顶部导航栏是无法支持自定义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;
}