参考链接:
(1)微信小程序底部导航Tabbar
https://www.cnblogs.com/huangjialin/p/6278429.html
(2)小程序自定义tabbar实现中间图标突出效果(附带wx.hideTabBar不生效的bug解决方案)
https://www.jianshu.com/p/ce1c3c25527c
(3)微信小程序入门四: 顶部、导航栏样式、tabBar导航栏
https://blog.csdn.net/haibo0668/article/details/80651680
(4)微信小程序自定义tabbar组件点击对应区域显示凸起效果
https://blog.csdn.net/nan961136007/article/details/103913052
一、例子1
底部导航栏这个功能是非常常见的一个功能,基本上一个完成的app,都会存在一个导航栏,那么微信小程序的导航栏该怎么实现呢?经过无数的踩坑,终于实现了,好了,先看看效果图。
对于底部导航栏,小程序上给出的文档要求里面的item最少2个,最多五个。
{
"pages":[
"pages/index/index",
"pages/logs/logs",
"pages/mine/mine"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "首页",
"navigationBarTextStyle":"black"
},
"tabBar": {
"color": "#a9b7b7",
"selectedColor": "#11cd6e",
"borderStyle": "black" ,
"list": [{
"selectedIconPath": "images/icon_consult_press.png",
"iconPath": "images/icon_consult.png",
"pagePath": "pages/index/index",
"text": "首页"
}, {
"selectedIconPath": "images/icon_invest_press.png",
"iconPath": "images/icon_invest.png",
"pagePath": "pages/logs/logs",
"text": "一元投"
},{
"selectedIconPath": "images/icon_mine_press.png",
"iconPath": "images/icon_mine.png",
"pagePath": "pages/mine/mine",
"text": "我的"
}
]
}
}
解释一下这些属性是什么意思:
- tabBar 指底部的 导航配置属性
- color 未选择时 底部导航文字的颜色
- selectedColor 选择时 底部导航文字的颜色
- borderStyle 底部导航边框的样色(注意 这里如果没有写入样式 会导致 导航框上边框会出现默认的浅灰色线条)
- list 导航配置数组
- selectedIconPath 选中时 图标路径
- iconPath 未选择时 图标路径
- pagePath 页面访问地址
- text 导航图标下方文字
需要注意一些小问题:
- 每个页面的json文件都不能去掉navigationBarTitleText这个属性。否则会报错
"pages":[ "pages/index/index", "pages/logs/logs", "pages/mine/mine" ],
这个页面的注册一定要注意,第一个一定是要是最先显示的,否则会出现底部导航看不到。
二、例子2
github传送门 https://github.com/SuRuiGit/wxapp-customTabbar
集成到项目
第一步:找到项目中的tabbarComponent目录,拷贝到你的工程中,然后将tabbarComponent->icon图标替换成你自己的tabbar图片(建议把tabbarComponent目录与pages同级)
第二步:到app.json中配置tabBar,如果tabbar中间的按钮是跳到二级页面,就不要配置在tabBar的list中,如果配置了,中间的按钮点击是无效的,要注意。
第三步:在app.js的onLaunch方法中调用hidetabbar()自己封装一下,代码如下,隐藏系统自带的tabBar直接写wx.hideTabBar在安卓8.0有bug,并且在每一个用到tabbar的js文件中的onReady和onShow中调用一下app.hidetabbar(),调用getSystemInfo()方法获取设备信息,主要是为了适配iPhone X,这个方法在下面会给出。
//自己对wx.hideTabBar的一个封装
hidetabbar() {
wx.hideTabBar({
fail: function() {
setTimeout(function() { // 做了个延时重试一次,作为保底。
wx.hideTabBar()
}, 500)
}
});
},
getSystemInfo: function() {
let t = this;
wx.getSystemInfo({
success: function(res) {
t.globalData.systemInfo = res;
}
});
},
第四步:在app.js中的globalData中加入自定义tabbar和systemInfo的参数,再加入一个方法给tabBar.list配置中的页面使用,代码如下。
editTabbar: function() {
let tabbar = this.globalData.tabBar;
let currentPages = getCurrentPages();
let _this = currentPages[currentPages.length - 1];
let pagePath = _this.route;
(pagePath.indexOf('/') != 0) && (pagePath = '/' + pagePath);
for (let i in tabbar.list) {
tabbar.list[i].selected = false;
(tabbar.list[i].pagePath == pagePath) && (tabbar.list[i].selected = true);
}
_this.setData({
tabbar: tabbar
});
},
globalData: {
systemInfo: null,//客户端设备信息
tabBar: {
"backgroundColor": "#ffffff",
"color": "#979795",
"selectedColor": "#1c1c1b",
"list": [
{
"pagePath": "/page/index/index",
"iconPath": "icon/icon_home.png",
"selectedIconPath": "icon/icon_home_HL.png",
"text": "首页"
},
{
"pagePath": "/page/myRelease/index",
"iconPath": "icon/icon_release.png",
"isSpecial": true,
"text": "发布"
},
{
"pagePath": "/page/mine/index",
"iconPath": "icon/icon_mine.png",
"selectedIconPath": "icon/icon_mine_HL.png",
"text": "我的"
}
]
}
}
第五步:在tabBar的list中配置的页面的.js文件的data中加入tabbar:{},并在onload方法中调用app.editTabbar();
第六步:在tabBar的list中配置的页面的.json文件中加入"usingComponents": { “tabbar”: “…/…/tabbarComponent/tabbar” }在.wxml文件中加入
第七步:修改tabbarComponent->tabbar.js->data中的代码,代码截图如下
这里就是作者的一个小bug,本来作者写的是==‘iPhone X’,在模拟器上没有问题,但是真机上返回的却不是这个字段,所以我们要改成包含
三、例子3
小程序的导航栏样式在app.json中定义。这里设置导航,背景黑色,文字白色,文字内容测试小程序
app.json内容:
{
"pages":[
"pages/index/index",
"pages/login/login",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"red",
"navigationBarBackgroundColor": "#000",
"navigationBarTitleText": "测试小程序",
"navigationBarTextStyle":"#fff"
}
}
tabBar挺好的,可以放置于顶部或者底部,用于不同功能页面的切换。tabBar同样在app.json中进行定义,看一下我在app.json中对tabBar的相关定义:
"tabBar": {
"selectedColor": "#1296db",
"list": [{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "images/ico-home.png",
"selectedIconPath": "images/ico-home-d.png"
},{
"pagePath": "pages/setting/setting",
"text": "设置",
"iconPath": "images/ico-setting.png",
"selectedIconPath": "images/ico-setting-d.png"
},{
"pagePath": "pages/help/help",
"text": "帮助",
"iconPath": "images/ico-help.png",
"selectedIconPath": "images/ico-help-d.png"
}]
}
四、例子4
首先呢,先创建template文件夹,里面包含wxml,wxss和 js。
wxml文件
<view class='footer'>
<view class='footer_list' data-current="{{0}}" bindtap="chooseImg">
<image class="footer-image" hidden='{{curIdx===0}}' src="{{listInfo[0].imgUrl}}"></image>
<view hidden='{{curIdx!==0}}' class="tab-img">
<image class="footer-image2" src="{{listInfo[0].curUrl}}"></image>
</view>
<!-- 等于该id的话图标和名称就凸起来 -->
<view class="footer-text">首页</view>
<view hidden='{{curIdx!==0}}' class="footer-text2">首页</view>
</view>
<view class='footer_list' data-current="{{1}}" bindtap="chooseImg">
<image class="footer-image" hidden='{{curIdx===1}}' src="{{listInfo[1].imgUrl}}"></image>
<view hidden='{{curIdx!==1}}' class="tab-img">
<image class="footer-image2" src="{{listInfo[1].curUrl}}"></image>
</view>
<view class="footer-text">分类</view>
<view hidden='{{curIdx!==1}}' class="footer-text2">分类</view>
</view>
<view class='footer_list' data-current="{{2}}" bindtap="chooseImg">
<image class="footer-image" hidden='{{curIdx===2}}' src="{{listInfo[2].imgUrl}}"></image>
<view hidden='{{curIdx!==2}}' class="tab-img">
<image class="footer-image2" src="{{listInfo[2].curUrl}}"></image>
</view>
<view class="footer-text">购物车</view>
<view hidden='{{curIdx!==2}}' class="footer-text2">购物车</view>
</view>
<view class='footer_list' data-current="{{3}}" bindtap="chooseImg">
<image class="footer-image" hidden='{{curIdx===3}}' src="{{listInfo[3].imgUrl}}"></image>
<view hidden='{{curIdx!==3}}' class="tab-img">
<image class="footer-image2" src="{{listInfo[3].curUrl}}"></image>
</view>
<view class="footer-text">个人中心</view>
<view hidden='{{curIdx!==3}}' class="footer-text2">个人中心</view>
</view>
</view>
wxss文件
page {
background: #f8f7f6;
}
.footer {
position: fixed;
bottom: 0;
width: 100%;
height: 105rpx; /*footer的高度*/
background: #fff;
z-index: 500;
border-top: 1rpx solid #e9e9e9;
display: flex;
flex-direction: row;
}
.footer_list {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
}
.footer-image {
width: 50rpx;
height: 50rpx;
margin-top: 10rpx;
}
.footer-image2 {
width: 50rpx;
height: 50rpx;
margin-top: 20rpx;
}
.footer-text2 {
font-size: 24rpx;
margin-top: 15rpx;
color: #896D59;
}
.tab-img {
position: absolute;
top: -36rpx;
width: 96rpx;
height: 96rpx;
border-radius: 50%;
border-top: 2rpx solid #f2f2f3;
background-color: #fff;
text-align: center;
box-sizing: border-box;
padding: 6rpx;
}
.footer-text {
font-size: 24rpx;
margin-top: 10rpx;
color: #896D59;
}
js文件
// pages/template/template.js
Page({
/**
* 页面的初始数据
*/
data: {
listInfo: [{
text: '首页',
imgUrl: '/pages/images/main.png',
curUrl: '/pages/images/mian_lo1_on.png',
},
{
text: '分类',
imgUrl: '/pages/images/mian_lo2.png',
curUrl: '/pages/images/mian_lo2_on.png',
},
{
text: '购物车',
imgUrl: '/pages/images/mian_lo3.png',
curUrl: '/pages/images/mian_lo3_on.png',
},
{
text: '个人中心',
imgUrl: '/pages/images/mian_lo4.png',
curUrl: '/pages/images/mian_lo4_on.png',
}]
},
// 底部导航
chooseImg: function (e) {
var that = this
this.setData({
curIdx: e.currentTarget.dataset.current
})
console.log("kk", e.currentTarget.dataset.current)
if (e.currentTarget.dataset.current == 0) {
wx.switchTab({
url: '/pages/index/index',
})
} else if (e.currentTarget.dataset.current == 1) {
wx.switchTab({
url: '/pages/classify/classify',
})
} else if (e.currentTarget.dataset.current == 2) {
wx.switchTab({
url: '/pages/car/car',
})
} else if (e.currentTarget.dataset.current == 3) {
wx.switchTab({
url: '/pages/person/person',
})
}
that.onShow();
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that = this
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
this.onLoad();
},
})
app.json文件
{
"pages": [
"pages/index/index",
"pages/classify/classify",
"pages/car/car",
"pages/person/person",
"pages/template/template"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "你们项目的名字",
"navigationBarTextStyle": "black"
},
"tabBar": {
"backgroundColor": "#ffffff",
"color": "#896D59",
"selectedColor": "#896D59",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "pages/images/main.png",
"selectedIconPath": "pages/images/mian_lo1_on.png"
},
{
"pagePath": "pages/classify/classify",
"text": "分类",
"iconPath": "pages/images/mian_lo2.png",
"selectedIconPath": "pages/images/mian_lo2_on.png"
},
{
"pagePath": "pages/car/car",
"text": "购物车",
"iconPath": "pages/images/mian_lo3.png",
"selectedIconPath": "pages/images/mian_lo3_on.png"
},
{
"pagePath": "pages/person/person",
"text": "个人中心",
"iconPath": "pages/images/mian_lo4.png",
"selectedIconPath": "pages/images/mian_lo4_on.png"
}
]
},
"sitemapLocation": "sitemap.json"
}
app.js文件
//app.js
App({
onLaunch: function () {
//隐藏系统tabbar
wx.hideTabBar();
// 展示本地存储能力
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
// 获取用户信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
wx.getUserInfo({
success: res => {
// 可以将 res 发送给后台解码出 unionId
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
}
}
})
},
onShow: function () {
//隐藏系统tabbar
wx.hideTabBar();
},
globalData: {
userInfo: null
}
})
app.js的重点是隐藏小程序自带的tabbar,让你自定义的组件得以显示,并且不被占位置。
然后是每个界面的引用了,你想在哪个界面显示这个tabbar,就放进去以下代码。比如index首页嘛,记得看你们的层级引用模板文件哦,不然会报错的。
wxml文件
<import src='../../template/template.wxml' />
<view>
<include src="/pages/template/template.wxml" />
</view>
wxss文件
@import "../template/template.wxss";
js文件
Page({
data: {
listInfo: [{
text: '首页',
imgUrl: '/pages/images/main.png',
curUrl: '/pages/images/mian_lo1_on.png',
},
{
text: '分类',
imgUrl: '/pages/images/mian_lo2.png',
curUrl: '/pages/images/mian_lo2_on.png',
},
{
text: '购物车',
imgUrl: '/pages/images/mian_lo3.png',
curUrl: '/pages/images/mian_lo3_on.png',
},
{
text: '个人中心',
imgUrl: '/pages/images/mian_lo4.png',
curUrl: '/pages/images/mian_lo4_on.png',
}],
// 底部导航
chooseImg: function(e) {
var that = this
this.setData({
curIdx: e.currentTarget.dataset.current
})
console.log("kk", e.currentTarget.dataset.current)
if (e.currentTarget.dataset.current == 0) {
wx.switchTab({
url: '/pages/index/index',
})
} else if (e.currentTarget.dataset.current == 1) {
wx.switchTab({
url: '/pages/classify/classify',
})
} else if (e.currentTarget.dataset.current == 2) {
wx.switchTab({
url: '/pages/car/car',
})
} else if (e.currentTarget.dataset.current == 3) {
wx.switchTab({
url: '/pages/person/person',
})
}
that.onShow();
},
onLoad: function(options) {
var curIdx = 0
this.setData({
curIdx: curIdx,
})
},
onShow: function() {
this.onLoad();
},
})