微信小程序自定义顶部导航栏
前言
现成轮子网上很多,但是想实现自己的需求,还得自己再造一遍轮子,有一篇文章写的很好:微信小程序自定义navigationBar
效果图
亮点
- ✅保证
安卓
和IOS
显示效果一致 - 拓展🉑️同时显示
返回上一页
和返回首页
- 点击标题🉑️返回顶部
- 预留slot进行拓展
代码
index.js
Component({
properties: {
title: { // 标题
type: String,
value: 'hello world',
},
showGoBack: { // 显示返回按钮
type: Boolean,
value: false,
},
showHome: { // 显示返回主页按钮
type: Boolean,
value: false,
},
position: { // 是否开启绝对定位
type: Boolean,
value: false,
},
bgColor: { // 导航栏背景颜色
type: String,
value: 'transparent',
},
titleColor: { // 导航栏标题颜色
type: String,
value: '#000000',
},
showSlot: { // 是否开启slot
type: Boolean,
value: false,
},
showBlankBlock: { // 是否开启空白块占位
type: Boolean,
value: false,
},
},
lifetimes: {
async attached() {
const systemInfo = await wx.getSystemInfo()
const {
statusBarHeight,
windowWidth
} = systemInfo
const rightCapsule = wx.getMenuButtonBoundingClientRect()
const {
top,
right,
width,
height
} = rightCapsule
const navBarYPadding = top - statusBarHeight
const navBarXPadding = windowWidth - right
const navBarHeight = height + (navBarYPadding * 2)
const navBarBodyHeight = height
const rightCapsuleWidth = width
const styleData = `--bgColor: ${this.properties.bgColor};
--statusBarHeight: ${statusBarHeight}px;
--navBarHeight: ${navBarHeight}px;
--navBarBodyHeight: ${navBarBodyHeight}px;
--navBarYPadding: ${navBarYPadding}px;
--navBarXPadding: ${navBarXPadding}px;
--rightCapsuleWidth: ${rightCapsuleWidth}px;
--rightCapsuleHeight: ${navBarBodyHeight}px;
--rightCapsuleRadius: ${navBarBodyHeight / 2}px;
--titleColor: ${this.properties.titleColor};
`
const backIconUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAY9JREFUWEfFl88rBVEUgL/3Lyj/lbLzI6UoSlGEKAsWihBFKUpRFspCSSkWxIJYKIqFYqFYkJWsJJ06V6ep9969c+e9O6upmeb7zo85c6ZE4qOUmE+tBBo0sM9qAdZCYAqYVHAXsFlJomiBaWDCAAUuEmWPIgVmgTFD+gWagMN6CMwDIwb0DbQD+/XogSVg0ICk8QR+VA0u12NLsAL0G9Ab0Aac+cBjBVaBPgN6VviVLzxGYB3oMaAHhd+GwPMKbADdBnQDtAKPofA8AltApwFdAi3ASx54qMA20GFApwp/zwsPEdjRNDuWDBeJ/CsG7iuwAAwb0J7Cf2LhvgKjwFxKAWEnLYELPtuEJ1qKj5hShI7i7Gt4oc1Zl9fQBZp0EDmJNaDXpP5eR/FdaDlCS2Cfn/0YPanEdYhEjIBwloEBA3xViXNfiVgB4SwCQwYoC4nsBMc+EkUICCfpSuYCnQHGTdSylDYDB5UyUVQGHCO7lu/qsCrrULSAgJL+mLhIG/Wk6q5Qiwz4NP//PckF/gCIUUwhlNMi4AAAAABJRU5ErkJggg=='
const homeIconUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAidJREFUWEft10+oD1EUwPHPW2BlI5KdkiQ7C2+l2LDxr+SxIJSQEiURCyz0ZEFECPm78KdeSVYSKcXKBjslxYJkJRtJ53VH86aZ38xv3u+3+53Vnbn3nPO95557zsyQ9rIJuzAdj3GijamhNkrYjUsF3TM40K29NgCHMZocHcXf3PNV7OwGoluAUziUHOzD+TTeg4tpfA9b8KcJSDcAV3K724HrBQebcQ3T8CRB/KyDaAIwFXcwgt/YjvsVhtckiFl4mSA+d4KoA5iN21iB78l57K6TLMUNzMPbBPG+SqETwILkfAk+IUL8qi6kaX4h7mIxPibd12W6VQDDKezz8Q5x5yt3UQE1Ew+xDN8SxNPi2jKAlcl5nOMbrMeXhjsvW/YIkRuRPxHFsfyiIsDG5HwKnmEtfk3CeaYaSRzOQ7bhVjaRB4iyejlNBPW6HjjOmziL/enFXlyIcQYQxSWKTEjQBWU/5AhOJsMxHg2AVamZxPtzOcp+AITNKNVR1EJWB8BxHCvpZvG+l1K0N+4zACLcUTjy8gLLe+kdz9OVzJsdyXJgAxalmSDrJ0D23fAAH4rXMIpGkPYTICIb9selDUAc2daK44kbdLNiLjuCSQEcxOma3IgQlyVwTwDyRso4Oh1fTwHKekhd/gwABhEYRGAQgUYRmIEfNaW2qlHNwdcGLXxCESuraNFsoiXPLTEWzqPW/+9mhTXxfxitPb6oixJw8fM6oU/8A8+oq0Iv6rN4AAAAAElFTkSuQmCC'
this.setData({
statusBarHeight,
navBarHeight,
navBarBodyHeight,
navBarYPadding,
navBarXPadding,
rightCapsuleWidth,
styleData,
backIconUrl,
homeIconUrl,
})
},
},
data: {
statusBarHeight: 20, // 状态栏高度
navBarHeight: 40, // 导航栏高度
navBarBodyHeight: 32, // 导航栏内容高度
navBarYPadding: 4, // 导航栏Y轴内间距
navBarXPadding: 8, // 导航栏X轴内间距
rightCapsuleWidth: 87, // 右边胶囊宽度
styleData: '', // 样式变量
backIconUrl: '', // 返回icon
homeIconUrl: '', // 主页icon
},
methods: {
goBack() {
// console.log('goBack');
const pages = getCurrentPages()
// 单曲页面栈小于等于一个页面,直接返回首页
if (pages.length <= 1) {
this.goHome()
} else {
wx.navigateBack()
}
},
goHome() {
// console.log('goHome');
// 小程序中页面栈最多十层
// 返回的页面数,如果 delta 大于现有页面数,则返回到首页。
wx.navigateBack({
delta: 10
})
},
backTop() {
wx.pageScrollTo({
scrollTop: 0,
duration: 300
})
},
}
})
index.wxml
<view style="{{styleData}}">
<view class="c-navbar {{position ? 'c-navbar--position' : ''}}">
<!-- 状态栏 -->
<view class="c-navbar__status-bar"></view>
<!-- 导航栏 -->
<view class="c-navbar__body" wx:if="{{!showSlot}}">
<!-- 导航栏左边 => 胶囊 -->
<view class="c-navbar__body__left">
<view class="c-navbar__body__left__back" wx:if="{{showGoBack && (!showHome)}}" bindtap="goBack">
<image src="{{backIconUrl}}" class="c-navbar__body__icon c-navbar__body__left__icon--back"></image>
<text>返回</text>
</view>
<view class="c-navbar__body__left__home" wx:if="{{showGoBack && showHome}}">
<view class="c-navbar__body__left__home__back" bindtap="goBack">
<image src="{{backIconUrl}}" class="c-navbar__body__icon c-navbar__body__left__home--back"></image>
</view>
<view class="c-navbar__body__left__home--division"></view>
<view class="c-navbar__body__left__home__home" bindtap="goHome">
<image src="{{homeIconUrl}}" class="c-navbar__body__icon c-navbar__body__left__home--home"></image>
</view>
</view>
</view>
<!-- 导航栏中间 => 标题 -->
<view class="c-navbar__body__title" bindtap="backTop">
{{title}}
</view>
<!-- 导航栏右边 => 胶囊 -->
<view class="c-navbar__body__right"></view>
</view>
<!-- 开启slot -->
<view class="c-navbar__body" wx:if="{{showSlot}}">
<view class="c-navbar__body--slot">
<slot></slot>
</view>
<!-- 导航栏右边 => 胶囊 -->
<view class="c-navbar__body__right"></view>
</view>
</view>
<!-- 空白块占位符, 用于解决下拉刷新导航栏掉下来的问题, 需要配合开启position -->
<view class="c-blank-block" wx:if="{{showBlankBlock}}"></view>
</view>
index.wxss
.c-navbar {
position: -webkit-sticky;
position: sticky;
top: 0;
left: 0;
z-index: 9999;
width: 100vw;
box-sizing: border-box;
background-color: var(--bgColor);
}
.c-navbar--position {
position: fixed;
}
.c-navbar__status-bar {
width: 100vw;
height: var(--statusBarHeight);
}
.c-navbar__body {
width: 100vw;
height: var(--navBarHeight);
padding: 0 var(--navBarXPadding);
overflow: hidden;
box-sizing: border-box;
overflow: hidden;
display: -webkit-flex;
display: flex;
/* -webkit-justify-content: center;
justify-content: center; */
-webkit-align-items: center;
align-items: center;
flex-wrap: wrap;
}
.c-navbar__body__left {
width: var(--rightCapsuleWidth);
height: var(--rightCapsuleHeight);
box-sizing: border-box;
font-size: 16px;
font-weight: 500;
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
flex-wrap: wrap;
}
.c-navbar__body__title {
box-sizing: border-box;
flex: 1;
padding: 0 var(--navBarXPadding);
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--titleColor);
font-size: 18px;
font-weight: 500;
}
.c-navbar__body__right {
width: var(--rightCapsuleWidth);
height: var(--rightCapsuleHeight);
}
.c-navbar__body__icon {
display: inline-block;
width: 20px;
height: 20px;
}
.c-navbar__body__left__back {
height: 100%;
background-color: rgba(255, 255, 255, .6);
border: 1px solid rgba(0, 0, 0, .1);
border-radius: var(--rightCapsuleRadius);
box-sizing: border-box;
padding: 0 calc(var(--navBarXPadding) * 2);
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
flex-wrap: wrap;
}
.c-navbar__body__left__icon--back {
margin-left: -5px;
}
.c-navbar__body__left__home {
position: relative;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, .6);
border: 1px solid rgba(0, 0, 0, .1);
border-radius: var(--rightCapsuleRadius);
box-sizing: border-box;
display: -webkit-flex;
display: flex;
}
.c-navbar__body__left__home__back {
flex: 1;
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
flex-wrap: wrap;
}
.c-navbar__body__left__home__home {
flex: 1;
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
flex-wrap: wrap;
}
.c-navbar__body__left__home--back {
width: 22px;
height: 22px;
box-sizing: border-box;
}
.c-navbar__body__left__home--home {
width: 19px;
height: 19px;
box-sizing: border-box;
}
.c-navbar__body__left__home--division {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 1px;
height: 20px;
background-color: rgba(0, 0, 0, .1);
}
.c-navbar__body--slot {
flex: 1;
}
.c-blank-block {
width: 100vw;
height: calc(var(--statusBarHeight) + var(--navBarHeight));
background-color: transparent;
}
index.json
{
"component": true,
"usingComponents": {}
}
属性
属性 | 描述 | 默认值 |
---|---|---|
title | 标题 | hello world |
showGoBack | 显示返回按钮 | false |
showHome | 显示返回主页按钮 | false |
position | 是否开启绝对定位 | false |
bgColor | 导航栏背景颜色 | white |
titleColor | 导航栏标题颜色 | #000000 |
showSlot | 是否开启slot | false |
showBlankBlock | 是否开启空白块占位 | false |
使用
index.json
{
"usingComponents": {
"wmp-navbar": "@chenbz/wmp-navbar"
},
"navigationStyle": "custom"
}
index.wxml
<view>
<wmp-navbar title="标题"></wmp-navbar>
</view>
npm
组件已打包发布到npm,欢迎移步至@chenbz/wmp-navbar