微信小程序底部菜单栏是可以通过自定义来实现的。主要涉及:新建组件、编写组件代码、设置样式、配置导航栏、在页面中引用。自定义底部导航栏可以创建出符合项目设计需求效果,实现个性化的页面切换功能。
如下图效果,为比较常见的中间突出半圆效果的扫码或拍照的功能键,这个是可以通过CSS进行绘制出来的。此篇,将通过案例讲解这个导航栏如何实现。
此案例中实现功能,与之前一篇实现顶部导航栏的方式雷同,有兴趣朋友可以去了解下,地址:微信小程序 - 自定义头部导航栏开发-CSDN博客
一、配置导航栏
在app.json文件中配置navigationStyle为custom,代码如下:
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/mine/index"
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin",
"navigationBarBackgroundColor": "#ffffff",
"navigationStyle": "custom"
},
"style": "v2"
}
二、新建组件
在项目components目录中,创建Navigation底部导航栏组件,如下图:
然后在app.json中,将其配置为全局组件,代码如下:
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/mine/index"
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin",
"navigationBarBackgroundColor": "#ffffff",
"navigationStyle": "custom"
},
"usingComponents": {
"custom-header": "./components/Header/index",
"custom-navigation": "./components/Navigation/index"
},
"style": "v2"
}
然后页面中就可以直接使用custom-navigation引用了,代码如下:
<custom-navigation></custom-navigation>
三、绘制背景效果
在页面中引入后,则可以进行编写菜单栏组件代码了,首先绘制出背景框。
index.wxml代码如下:
<!--components/Navigation/index.wxml-->
<view class="navigation-wrap">
<view class="navigation-empty-wrap">
<view class="empty-box"></view>
<view class="bottom-safet-area" style="height: {{emptyHeight}}rpx;"></view>
</view>
<view class="fixed-nav-wrap">
<view class="shadow-box">
<view class="middle-circle"></view>
</view>
<view class="bottom-safet-area" style="height: {{emptyHeight}}rpx;"></view>
</view>
</view>
index.wxss代码如下:
/* components/Navigation/index.wxss */
.navigation-wrap{ width: 100%; }
.fixed-nav-wrap{ width: 100%; position: fixed; left: 0; bottom: 0; z-index: 100; }
.fixed-nav-wrap .shadow-box{
width: 100%;
min-height: 100rpx;
background-color: #ffffff;
box-shadow: 0 0 10rpx rgba(0, 0, 0, .2);
position: relative;
z-index: 1;
}
.fixed-nav-wrap .shadow-box .middle-circle{
width: 150rpx;
height: 150rpx;
margin-left: -75rpx;
border-radius: 100rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, .2);
background-color: #ffffff;
position: absolute;
left: 50%;
top: -45rpx;
z-index: 1;
}
.navigation-empty-wrap{ width: 100%; }
.navigation-empty-wrap .empty-box{ width: 100%; height: 100rpx; }
.bottom-safet-area{ background-color: #FFF; }
此时页面效果背景轮廓就出来了,虽然现在还不太美观,后期还需再度修饰一下。如下图:
这里注意的几个容器:
- navigation-empty-wrap类选择器空容器,为了解决悬浮导航不遮挡超出屏幕的内容,将页面中内容撑到底部菜单栏上面;
- fixed-nav-wrap类选择器为悬浮的菜单栏的最外层容器;
- shadow-box类选择器为底部轮廓样式(包含中间半圆样式);
- bottom-safet-area类选择器,为解决部分机型底部存在突出部分,如下图:
四、完成轮廓绘制
如上图可见,中间圆环,以及当底部有突出部分时出现的阴影,可以使用菜单容器进行覆盖,叠加在上层即可。
首先index.wxml中增加fixed-nav-content类容器,用于渲染菜单栏内容的容器,代码如下:
<!--components/Navigation/index.wxml-->
<view class="navigation-wrap">
<view class="navigation-empty-wrap">
<view class="empty-box"></view>
<view class="bottom-safet-area" style="height: {{emptyHeight}}rpx;"></view>
</view>
<view class="fixed-nav-wrap">
<view class="shadow-box">
<view class="middle-circle"></view>
</view>
<view class="fixed-nav-content">
</view>
<view class="bottom-safet-area" style="height: {{emptyHeight}}rpx;"></view>
</view>
</view>
然后index.wxss中添加fixed-nav-content类选择器样式,代码如下:
/* components/Navigation/index.wxss */
.navigation-wrap{ width: 100%; }
.fixed-nav-wrap{ width: 100%; position: fixed; left: 0; bottom: 0; z-index: 100; }
.fixed-nav-wrap .shadow-box{
width: 100%;
min-height: 100rpx;
background-color: #ffffff;
box-shadow: 0 0 10rpx rgba(0, 0, 0, .2);
position: relative;
z-index: 1;
}
.fixed-nav-wrap .shadow-box .middle-circle{
width: 150rpx;
height: 150rpx;
margin-left: -75rpx;
border-radius: 100rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, .2);
background-color: #ffffff;
position: absolute;
left: 50%;
top: -45rpx;
z-index: 1;
}
.navigation-empty-wrap{ width: 100%; }
.navigation-empty-wrap .empty-box{ width: 100%; height: 100rpx; }
.bottom-safet-area{ background-color: #FFF; }
.fixed-nav-content{
background-color: #ffffff;
width: 100%;
height: 120rpx;
position: absolute;
left: 0;
top: 0;
z-index: 5;
}
最后页面中则呈现出一个完美的底部菜单栏轮廓了,如下图:
五、菜单内容渲染
可以在index.js增加菜单数据,在容器中渲染菜单列表数据。在类容器fixed-nav-content中,创建模拟table类容器用于渲染菜单内容。
对于菜单列表需要对每项进行平分宽度,可以使用flex布局,也可以使用table + table-cell模拟表格属性实现。这里使用后者,table中的cell设置width:1%时,所有列会平分是因为会默认添加一个隐匿的table布局,其布局方式为table-layout: auto,且每个单元格有自己的最小宽度。
index.wxml代码如下:
<!--components/Navigation/index.wxml-->
<view class="navigation-wrap">
<view class="navigation-empty-wrap">
<view class="empty-box"></view>
<view class="bottom-safet-area" style="height: {{emptyHeight}}rpx;"></view>
</view>
<view class="fixed-nav-wrap">
<view class="shadow-box">
<view class="middle-circle"></view>
</view>
<view class="fixed-nav-content">
<view class="table">
<view class="cell {{item.id===navId?'active':''}}" wx:for="{{navList}}" wx:key="index" data-id="{{item.id}}" bind:tap="jump2Event">
<block wx:if="{{item.id==-1}}">
<image src="../../images/scan.png" mode="aspectFill" class="scan" />
</block>
<block wx:else>{{item.name}}</block>
</view>
</view>
</view>
<view class="bottom-safet-area" style="height: {{emptyHeight}}rpx;"></view>
</view>
</view>
index.wxss代码如下:
/* components/Navigation/index.wxss */
.navigation-wrap{ width: 100%; }
.fixed-nav-wrap{ width: 100%; position: fixed; left: 0; bottom: 0; z-index: 100; }
.fixed-nav-wrap .shadow-box{
width: 100%;
min-height: 100rpx;
background-color: #ffffff;
box-shadow: 0 0 10rpx rgba(0, 0, 0, .2);
position: relative;
z-index: 1;
}
.fixed-nav-wrap .shadow-box .middle-circle{
width: 150rpx;
height: 150rpx;
margin-left: -75rpx;
border-radius: 100rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, .2);
background-color: #ffffff;
position: absolute;
left: 50%;
top: -45rpx;
z-index: 1;
}
.navigation-empty-wrap{ width: 100%; }
.navigation-empty-wrap .empty-box{ width: 100%; height: 100rpx; }
.bottom-safet-area{ background-color: #FFF; }
.fixed-nav-content{
background-color: #ffffff;
width: 100%;
height: 120rpx;
position: absolute;
left: 0;
top: 0;
z-index: 5;
}
.fixed-nav-content .table{ display: table; width: 100%; height: 100rpx; font-size: 26rpx; }
.fixed-nav-content .table .cell{ display: table-cell; vertical-align: middle; width: 1%; text-align: center;}
.fixed-nav-content .table .cell.active{ color: #FF872E; }
.fixed-nav-content .scan{ width: 60rpx; height: 60rpx; position: relative; top: -20rpx; }
index.js代码如下:
// components/Navigation/index.js
Component({
/**
* 组件的初始数据
*/
data: {
navList: [
{id: 1, name: "首页", path: "/pages/index/index"},
{id: 2, name: "列表", path: ""},
{id: -1, name: "扫码", path: ""},
{id: 3, name: "订单", path: ""},
{id: 4, name: "我的", path: "/pages/mine/index"}
],
emptyHeight: 0
},
ready(){
this.initialHeight();
},
/**
* 组件的方法列表
*/
methods: {
// 获取底部安全距离
initialHeight(){
const sysInfo = wx.getSystemInfoSync();
this.setData({
emptyHeight: sysInfo.windowHeight - sysInfo.safeArea.bottom
})
}
}
})
注意:index.js中initialHeight()函数,则是用来初始化部分机型底部突出区域高度计算的。
此时页面菜单栏效果如下图:
六、实现跳转
此案例中,创建了首页和我的 两个页面,分别在里面引入custom-navigation组件。
在custom-navigation组件index.js中添加跳转事件,代码如下:
// components/Navigation/index.js
Component({
/**
* 组件的初始数据
*/
data: {
navList: [
{id: 1, name: "首页", path: "/pages/index/index"},
{id: 2, name: "列表", path: ""},
{id: -1, name: "扫码", path: ""},
{id: 3, name: "订单", path: ""},
{id: 4, name: "我的", path: "/pages/mine/index"}
],
emptyHeight: 0
},
ready(){
this.initialHeight();
},
/**
* 组件的方法列表
*/
methods: {
// 获取底部安全距离
initialHeight(){
const sysInfo = wx.getSystemInfoSync();
this.setData({
emptyHeight: sysInfo.windowHeight - sysInfo.safeArea.bottom
})
},
// 跳转
jump2Event(e){
const data = e.currentTarget.dataset,
currentNav = this.data.navList.filter(item => item.id == data.id)[0];
if(currentNav['path']) {
wx.reLaunch({
url: currentNav['path'],
})
} else if(data.id == -1){
// 扫码
wx.scanCode({
success: res => {
console.log('success', res);
},
fail: msg => {
console.error(msg);
}
});
}
// if end
}
}
})
当点击为中间扫码/拍照时,其data.id为-1,则启用摄像头;当点击其他菜单项时,如path链接存在,则实现跳转动作。
七、设置高亮
为实现进入某个页面,菜单栏上显示对应栏目的高亮效果,这则需要在每个引入页面定义菜单栏时,添加相应属性告诉组件需要将哪个菜单项添加高亮。
在组件中定义对外属性,navId为navList中每项对应的id值。代码如下:
// components/Navigation/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
navId: {
type: Number,
value: 1
}
},
// 略...
})
首页代码如下:
<custom-navigation navId="1"></custom-navigation>
我的页面代码如下:
<custom-navigation navId="4"></custom-navigation>
最后在custom-navigation组件的index.wxml中,通过id判断哪个为高亮部分。代码如下:
<view class="table">
<view class="cell {{item.id===navId?'active':''}}"
wx:for="{{navList}}"
wx:key="index"
data-id="{{item.id}}"
bind:tap="jump2Event">
<block wx:if="{{item.id==-1}}">
<image src="../../images/scan.png" mode="aspectFill" class="scan" />
</block>
<block wx:else>{{item.name}}</block>
</view>
</view>
当navId与item.id相等时,追加active类选择器,实现高亮效果。