1. 前言
小程序开发中,有时我们不需要自带的导航栏,那就需要自己写一个这样的导航栏。
2. 效果展示
3.文件目录结构
├── root
│ ├── components
│ │ └── navbar
│ │ ├── index.js
│ │ ├── index.wxml
│ │ ├── index.json
│ │ ├── index.wxss
│ ├──pages
│ │ ├── index.js
│ │ ├── index.wxml
│ │ ├── index.json
│ │ ├── index.wxss
└──── app.js
4.如何使用
pages/index
<navbar navbar-data="{{navbarData}}"></navbar>
Page({
data: {
navbarData: {
showCapsule: 1, //是否显示头部左上角小房子 1显示 0 不显示
showBack: 1, //是否显示返回 1显示 0不显示
// showColor: 0, //navbar背景颜色 1展示 0不展示
// navigationBarColor: 'yellow',
bgTransparent: true, // 时候否透明
iconColor: 'black',
titleColor: 'black',
title: '首页'
},
},
})
5. 实现代码
需要现在app.js启动的时候获取一些信息
app.js
// app.js
App({
onLaunch() {
const { statusBarHeight, titleBarHeight } = getStatusBarHeight()
console.log(statusBarHeight, titleBarHeight)
this.globalData.statusBarHeight = statusBarHeight;
this.globalData.titleBarHeight = titleBarHeight;
},
globalData: {
statusBarHeight: "",
titleBarHeight: ""
}
})
function getStatusBarHeight() {
let res = wx.getSystemInfoSync()
let customTitleBarHeight;
try {
const menuInfo = wx.getMenuButtonBoundingClientRect();
if (equalVersion(res.version, "7.0.3")) {
customTitleBarHeight =
menuInfo.height + (menuInfo.top - res.statusBarHeight) * 2;
} else {
customTitleBarHeight = menuInfo.height + menuInfo.top * 2;
}
} catch (e) {
customTitleBarHeight = 48;
}
if (res.model.indexOf("iPhone") !== -1) {
customTitleBarHeight = 44;
}
return {
statusBarHeight: res.statusBarHeight,
titleBarHeight: customTitleBarHeight,
}
};
function equalVersion(curV, reqV) {
let arr1 = curV.split(".");
let arr2 = reqV.split(".");
let maxL = Math.max(arr1.length, arr2.length);
let pos = 0;
let diff = 0;
while (pos < maxL) {
diff = parseInt(arr1[pos]) - parseInt(arr2[pos]);
console.log(diff, parseInt(arr1[pos]), parseInt(arr2[pos]));
if (diff != 0) {
break;
}
pos++;
}
if (diff > 0 || diff == 0) {
//新版本、稳定版
return 1;
} else {
// 旧版本
return 0;
}
}
下面是组件代码
component/navbar.js
/**
* 自定义头部
*/
const app = getApp()
// 版本号比较函数
function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
Component({
properties: {
navbarData: { //navbarData 由父页面传递的数据,变量名字自命名
type: Object,
value: {},
observer: function (newVal, oldVal, changedPath) {
if (newVal == null && typeof (oldVal) == 'object') {
try {
var _o = {};
var defaultData = {
statusBarHeight: '',
titleBarHeight: '',
navbarData: {
showCapsule: 1, //是否显示左上角小房子 1显示 0 不显示
showBack: 1, //是否显示返回 1显示 0不显示
showColor: 1, //navbar背景颜色 1蓝色 0白色
navigationBarColor: '#ffffff',
bgTransparent: false, // true 背景为透明 false 不透明(下面两个属性也不生效)
iconColor: 'black', // white 左侧icon颜色
titleColor: '#000000' // 可以指定title具体色值
}
}
_o['navibarData'] = defaultData['navbarData']
this.setData(_o);
} catch (e) {
}
} else {
// var data = this.data;
var data = {};
var path = 'navibarData';
Object.keys(newVal).forEach(key => {
data[`${path}.${key}`] = newVal[key];
})
this.setData(data);
}
}
}
},
data: {
statusBarHeight: '',
titleBarHeight: '',
showNavbar: '',
menuButtonInfo: {},
navibarData: {
showCapsule: 0, //是否显示左上角小房子
showBack: 1, //是否显示返回
showColor: 1, //navbar背景颜色,
navigationBarColor: '#ffffff',
bgTransparent: false, // true 背景为透明 false 不透明(下面两个属性也不生效)
iconColor: 'black', // white 左侧icon颜色
titleColor: '#000000' // 可以指定title具体色值
}
},
attached: function () {
if (wx.getSystemInfoSync().platform.toLowerCase().indexOf('devtools') != -1) {
this.setData({
showNavbar: true
});
} else {
// 获取版本号
const version = wx.getSystemInfoSync().version
if (compareVersion(version, '7.0.0') >= 0) {
// 版本号大于7.0.0时,显示自定义头部
this.setData({
showNavbar: true
})
} else {
// // 版本号低于7.0.0时,隐藏自定义头部
this.setData({
showNavbar: false
})
}
}
this.setData({
statusBarHeight: app.globalData.statusBarHeight,
titleBarHeight: app.globalData.titleBarHeight
});
//获取右上角胶囊的大小,为显示自定义icon做准备
this.setData({
menuButtonInfo: wx.getMenuButtonBoundingClientRect()
});
},
ready() {
try {
const currentPages = getCurrentPages()
const _o1 = {}
//无论如何返回按钮出现的规则不变
if (currentPages.length > 1) {
_o1.showBack = true;
} else {
_o1.showBack = false;
}
this.setData({
navibarData: {
...this.data.navibarData,
..._o1
}
})
} catch (e) {
}
},
methods: {
//返回到首页
_backhome(e) {
wx.switchTab({
url: '/pages/home/homepage',
});
},
_backlast(e) {
wx.navigateBack();
},
},
})
component/navbar.wxml
<view style="padding-top:{{navibarData.bgTransparent ? 0 : (statusBarHeight+titleBarHeight)}}px" wx:if='{{showNavbar}}'>
<view wx:if='{{navibarData.bgTransparent}}' class="nav-wrap b-nav-wrap transparent-bg">
<template is="titleBarContent" data="{{statusBarHeight,titleBarHeight,navibarData,menuButtonInfo}}"/>
</view>
<block wx:else>
<view wx:if='{{navibarData.showColor}}' class="nav-wrap" style='background-color:{{navibarData && navibarData.navigationBarColor}};'>
<template is="titleBarContent" data="{{statusBarHeight,titleBarHeight,navibarData,menuButtonInfo}}"/>
</view>
<view wx:else class="nav-wrap b-nav-wrap">
<template is="titleBarContent" data="{{statusBarHeight,titleBarHeight,navibarData,menuButtonInfo}}"/>
</view>
</block>
</view>
<template name="titleBarContent">
<view class="status-bar" style="height:{{statusBarHeight}}px"></view>
<view class="title-bar" style="height:{{titleBarHeight}}px">
<view class="title-bar-group {{navibarData.showBack ^ navibarData.showCapsule ? 'only-group-icon' : navibarData.showBack && navibarData.showCapsule ? '' :'no-group-icon'}}">
<view class="title-bar-icon" bindtap='_backlast' wx:if='{{navibarData.showBack}}'>
<image wx:if="{{navibarData.bgTransparent&&navibarData.iconColor == 'white'}}" src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAApVBMVEUAAAD///+4/eNVAAAANnRSTlMACPH9+vbrsCsF5LwTDALcPTYhAd/Ik4J4ZFRMRkIyG9LNt6SZjGAvFg/Z1cKnoIdyaFomm2zHx4MpAAABcElEQVRYw+2YaXKCQBhEhwFRcEXBfd8xxpiYzP2PltR0pbzA+5FU8Q7w/tD012AqKir+B8F8ifp6qXM70DeJ3A8NStcZOs8J8rWa8m0HjK/Rle81YHwz63Xh2DCMQ++zMyh9O+fpQg94tZGv2WJ8WSrfMGF8x5rXRe9Qmg/OE/cYX7mVb91nfI+LfPU245tb+UbQ23ZXmmtTKM0350mhSj0X8hVnxrdM5btB5fKhNId3KM1vzmPnjK9dl+/yYHz9F/m2JeNbxPIdOtDbESnNR8NQKi5xht1y79usDMXBd31gMPa+rTqc8MidIxHk3MEUmeVOumjk3OgQrSs3i0Syd3Qav6LnMUYbIpoYQXWYBg3Zsso4ewdyZRy8VFaHHlyGoaYIwaogx5LWNTrntP/pjM/scxKz31D7xMBfedczVj9DGfMT+qWsMUvRi7m5Lfrr34zT9VNgD3swknFhMKY1/XPgWHad+zQkySIzFRUVf41vKdVQ6T8ZiacAAAAASUVORK5CYII=' mode='aspectFit' class='back-home-return'></image>
<image wx:else src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAAkFBMVEUAAAAzMzMzMzM1NTU6Ojo/Pz8zMzMzMzNEREQzMzMzMzM0NDRnZ2czMzMzMzMzMzMzMzM0NDQ0NDQ0NDQ2NjY3Nzf///8zMzMzMzMzMzMzMzMzMzMzMzM0NDQ0NDQzMzM0NDQ1NTU9PT0zMzMzMzMzMzMzMzM0NDQzMzM3Nzc3Nzc0NDQzMzM0NDQ2NjYzMzP3PMnqAAAAL3RSTlMA+vErDwj2sATs3TYC5uHQuU09MSETAdfJvaWak4J4ZEZCC8OgjGlgVRwYh3JaJmpD+NcAAAFhSURBVFjD7ZdbjoJAFAUbfKEIylsFBRREHbX3v7sxfTtxA/Uxk1ALqB8qtw9qZGTkfzBrEtQXB1p3oK929IcJpVtXWpPC6VZ825zxTVbiO8wYX+SK76QYTqJzI6i+g/hW0Pe478S3zxifH4iv8hjfMDc65wrV3GvDMmZr3qSM712Ib3FmfI2t+ZgzvqdjdPMbVHOnDSF0Uh+25suDrbmDjsvL1vyEav6xx6WBal6Ir3gzvtTWvJ0yvnYpvn7N+Bpb80C9baHxBb6CqOW43BVFb279TGGU5lqtOeHAFSOcQ+7BFHyXe9KFSciNDiG7cLNI8EpN13h1vo8xeiGcGjOmBTlo5Mqik0veAbrx15xu3A/IKSKvPTmWZF2jc072P9145H4nMfsPVWKNZ3s7xLDGvQqcikINjlkhXtKNpxvb+BR7D+352XmUMT+KsVUYN3N+EsWRfBovFYnX+mpkZOSv8Qvxhkf6bCYPxwAAAABJRU5ErkJggg==' mode='aspectFit' class='back-home-return'></image>
</view>
<view class="line" wx:if='{{navibarData.showBack && navibarData.showCapsule}}'></view>
<view class="title-bar-icon" bindtap='_backhome' wx:if='{{navibarData.showCapsule}}'>
<image wx:if="{{navibarData.bgTransparent&&navibarData.iconColor == 'white'}}" src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAAXVBMVEUAAAD///9RKvvlAAAAHnRSTlMA9kNVhvo95i4VAuzgsHxjWhwH0Me9uaugcE82JiScUXyNAAABNElEQVRYw+3Wx3KEMBBF0YYZRYY4Oen/P9MuyviBEVNluhcOuuuno21T6tfW3e+dIOfOKgR1dlJeXoS+IhfysvBRlst6EPkeRFkPIt+DKOtB5HsQZT2IfA8i1+OL8PgiPDkRHl+EJyfC44vw5ER4fBGenAiPL8KTE+HxRXhyIjy+CE9OhMcX4cmJ8PgiPElRw1sr6rHnTWBnPKE2CNQS8ju+t/M0yh6Ki67WWpW+FAdLszZrwU3/PIEJ/O+gKrfvlUoKNJb6rBECm2HQCIFuGDghEIsEJvDvgAYL8z0wj89PWJzii5zitfF5jUW9eNXEO0Zvlg6DLnoDHWmpZzZfK02jtJovsictVqvZ+kaTbrM/VU0vsvvpunzQlx7ldLG39DKvr9uhqnEUyTXV5+SqPaVSP683QHRHUsVUZh0AAAAASUVORK5CYII=' mode='aspectFit' class='back-home'></image>
<image wx:else src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAAXVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5BxTwAAAAHnRSTlMA9kNVhvo95i4VAuzgsHxjWhwH0Me9uaugcE82JiScUXyNAAABNElEQVRYw+3Wx3KEMBBF0YYZRYY4Oen/P9MuyviBEVNluhcOuuuno21T6tfW3e+dIOfOKgR1dlJeXoS+IhfysvBRlst6EPkeRFkPIt+DKOtB5HsQZT2IfA8i1+OL8PgiPDkRHl+EJyfC44vw5ER4fBGenAiPL8KTE+HxRXhyIjy+CE9OhMcX4cmJ8PgiPElRw1sr6rHnTWBnPKE2CNQS8ju+t/M0yh6Ki67WWpW+FAdLszZrwU3/PIEJ/O+gKrfvlUoKNJb6rBECm2HQCIFuGDghEIsEJvDvgAYL8z0wj89PWJzii5zitfF5jUW9eNXEO0Zvlg6DLnoDHWmpZzZfK02jtJovsictVqvZ+kaTbrM/VU0vsvvpunzQlx7ldLG39DKvr9uhqnEUyTXV5+SqPaVSP683QHRHUsVUZh0AAAAASUVORK5CYII=' mode='aspectFit' class='back-home'></image>
</view>
</view>
<view style="flex-grow:1;height:100%;background-color: transparent; display: flex; justify-content:{{navibarData.iconData.align?(navibarData.iconData.align==='right'?'flex-end':'flex-start'):'flex-start'}};">
<view style="display: flex;align-items: center;height: 100%;">
</view>
</view>
<view style="width:{{menuButtonInfo.width}}px;height:100%;background-color: transparent;"></view>
<view class='nav-titleContainer'>
<view wx:if="{{navibarData.bgTransparent}}" class="nav-title" style="color:{{navibarData.titleColor}}">{{navibarData.title}}</view>
<view wx:else class="nav-title">{{navibarData.title}}</view>
</view>
</view>
</template>
component/navbar.wxss
/* 顶部要固定定位 */
.nav-wrap {
position: fixed;
width: 100%;
top: 0;
z-index: 99;
background: #ffffff;
}
/* 标题要居中 */
.nav-titleContainer {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
position: absolute;
top:0;
left:0;
height: 100%;
width: 100%;
pointer-events: none;
}
.nav-title {
margin: 0 180rpx;
text-align: center;
/* width: 50%; */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 36rpx;
color: #000000;
font-weight: 600;
z-index: -1;
}
.back-home {
width: 40rpx;
height: 40rpx;
cursor: pointer;
}
.back-home-return {
width: 40rpx;
height: 40rpx;
cursor: pointer;
}
.title-bar-group {
border: 1rpx solid rgba(255,255,255,0.25);
background: rgba(0,0,0,0.12);
border-radius: 32rpx;
display: flex;
height: 64rpx;
width: 174rpx;
box-sizing: border-box;
align-items:center;
overflow: hidden;
}
.title-bar-group.only-group-icon {
border: none;
background: transparent;
width: 87rpx;
}
.title-bar-group.no-group-icon {
border: none;
background: transparent;
width: 0rpx;
}
.title-bar-group.no-group-icon + .nav-titleContainer {
justify-content:flex-start;
}
.title-bar-group.no-group-icon + .nav-titleContainer .nav-title {
margin: 0 180rpx 0 12rpx;
text-align:left;
}
.title-bar-icon {
flex: auto;
display:flex;
align-items:center;
justify-content:center;
height:100%;
}
.line{
height: 38rpx;
border-left: 1px solid rgba(255,255,255,0.12);
}
.title-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 20rpx;
position: relative;
}
.b-nav-wrap.nav-wrap {
background: #fff;
}
.b-nav-wrap .nav-title {
color: #111;
}
.b-nav-wrap .title-bar-group {
border: 1rpx solid rgba(0,0,0,0.12);
background: #ffffff;
}
.b-nav-wrap .line{
border-left: 1px solid rgba(0,0,0,0.12);
}
.b-nav-wrap .title-bar-group.only-group-icon {
border: none;
background: transparent;
width: 87rpx;
}
.b-nav-wrap .title-bar-group.no-group-icon {
border: none;
background: transparent;
width: 0rpx;
}
.b-nav-wrap.nav-wrap.transparent-bg,
.b-nav-wrap.nav-wrap.transparent-bg .title-bar-group {
background: transparent;
}
6. 原理分析
wx.getSystemInfoSync();
// 获取到状态栏的信息
wx.getMenuButtonBoundingClientRect();
// 获取胶囊的信息
function getStatusBarHeight() {
let res = wx.getSystemInfoSync()
let customTitleBarHeight;
try {
const menuInfo = wx.getMenuButtonBoundingClientRect(); // 获取胶囊的信息
if (equalVersion(res.version, "7.0.3")) {
customTitleBarHeight =
menuInfo.height + (menuInfo.top - res.statusBarHeight) * 2;
} else {
customTitleBarHeight = menuInfo.height + menuInfo.top * 2;
}
} catch (e) {
customTitleBarHeight = 48;
}
if (res.model.indexOf("iPhone") !== -1) {
customTitleBarHeight = 44;
}
return {
statusBarHeight: res.statusBarHeight, // 状态栏的高度
titleBarHeight: customTitleBarHeight, // title的高度
}
};