概括
- 注意事项
- 项目结构
- 代码
- 适配低版本
参考文档:
小程序官方的tabBar文档
小程序官方的Component文档
小程序app.json的配置
注意事项
- 自定义tabBar 基础库 2.5.0 开始支持
- 自定义tabBar的组件一定要叫 custom-tab-bar 并且一定要与app.js同级
- 在 app.json 中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置也补充完整。
- 所有 tab 页的 json 里需声明usingComponents 项,也可以在 app.json 全局开启。
// app.json 记得删掉注释
"usingComponents": {
"nav": "/components/nav_bar/nav_bar" // 用与做低版本适配的 自定义tabBar组件
},
"tabBar": {
"custom": true, // 打开tabBar的自定义功能
"color": "#212121",
...
项目结构
- 代替原生tabBar的 custom-tab-bar 是不是一定要这样放?
是的,一定要放在与app.js同级的项目结构, 而且一定要叫 custom-tab-bar
先码为敬
custom-tab-bar
- wxml
<!--components/custom-tab-bar/index.wxml-->
<view class='my-bar ak-flexB {{isIpx ? "hackIPX" : ""}}'>
<view wx:for="{{list}}" wx:key="index" class='my-bar__item ak-flexC' data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab" data-jump_type='{{item.jumpType}}'>
<view class='my-bar__item-text ak-flex-columnC {{selected == index ? "my-bar__item-active" : ""}}'>
<image class='my-bar__btn-img animated' mode='widthFix' src='{{selected === index ? item.selectedIconPath : item.iconPath}}'></image>
{{item.text}}
<view hidden="{{item.tagNum <= 0}}" class='my-bar__item-tag ak-flexC'>
{{item.tagNum}}
</view>
</view>
</view>
</view>
- js
// components/custom-tab-bar/index.js
let app = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
now: {
type: String,
value: 'index'
},
cartNum: {
type: Number,
value: 0,
}
},
/**
* 组件的初始数据
*/
data: {
isIpx: app.globalData.isIpx, // 用于适配全面屏的底部高度(iPhone X* 的底部杠杠)
selected: 0, // 当前选中的项
color: "#333", // 未选中的字体的颜色
selectedColor: "#d01716", // 选中时的字体颜色
list: [{
pagePath: "/pages/index/index", // 跳转路径, 【switchTab的跳转一定要在app.json中配置】
iconPath: "/images/icon_index.png",
selectedIconPath: "/images/icon_index_act.png",
jumpType: "switchTab", // 跳转的类型
tagNum: 0,
text: "首页"
}, {
pagePath: "/pages/classificationII/classificationII",
iconPath: "/images/icon_classify.png",
selectedIconPath: "/images/icon_classify_act.png",
jumpType: "switchTab",
tagNum: 0,
text: "分类"
}, {
pagePath: "/pages/shoppingCart/shoppingCart",
iconPath: "/images/icon_cart.png",
selectedIconPath: "/images/icon_cart_act.png",
jumpType: "navigateTo",
tagNum: 0,
text: "购物车"
}, {
pagePath: "/pages/center2/center2",
iconPath: "/images/icon_my.png",
selectedIconPath: "/images/icon_my_act.png",
jumpType: "switchTab",
tagNum: 0,
text: "我的"
}]
},
/**
* 组件的方法列表
*/
methods: {
switchTab(e) {
const data = e.currentTarget.dataset
const url = data.path
const jumpType = data.jump_type;
wx[jumpType]({
url
})
this.setData({
selected: data.index
})
},
}
})
- wxss
/* components/custom-tab-bar/index.wxss */
/* 弹性盒居中 */
.animated {
-webkit-animation-duration: .7s;
animation-duration: .7s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
@keyframes bounceIn {
from, 20%, 40%, 60%, 80%,
to {
-webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
0% {
opacity: 0;
-webkit-transform: scale3d(0.3, 0.3, 0.3);
transform: scale3d(0.3, 0.3, 0.3);
}
20% {
-webkit-transform: scale3d(1.1, 1.1, 1.1);
transform: scale3d(1.1, 1.1, 1.1);
}
40% {
-webkit-transform: scale3d(0.9, 0.9, 0.9);
transform: scale3d(0.9, 0.9, 0.9);
}
60% {
opacity: 1;
-webkit-transform: scale3d(1.03, 1.03, 1.03);
transform: scale3d(1.03, 1.03, 1.03);
}
80% {
-webkit-transform: scale3d(0.97, 0.97, 0.97);
transform: scale3d(0.97, 0.97, 0.97);
}
to {
opacity: 1;
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
.bounceIn {
-webkit-animation-duration: 0.75s;
animation-duration: 0.75s;
-webkit-animation-name: bounceIn;
animation-name: bounceIn;
animation-delay: 0.26s;
}
.ak-flexC {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-justify-content: space-around;
justify-content: space-around;
-moz-box-pack: space-around;
-webkit--moz-box-pack: space-around;
box-pack: space-around;
align-items: center;
-webkit-align-items: center;
box-align: center;
-moz-box-align: center;
-webkit-box-align: center;
}
/* 弹性盒居两边 */
.ak-flexB {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-justify-content: space-between;
justify-content: space-between;
-moz-box-pack: space-between;
-webkit--moz-box-pack: space-between;
box-pack: space-between;
align-items: center;
-webkit-align-items: center;
box-align: center;
-moz-box-align: center;
-webkit-box-align: center;
}
/* 弹性盒纵向排列(居中) */
.ak-flex-columnC {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-justify-content: space-around;
justify-content: space-around;
-moz-box-pack: space-around;
-webkit--moz-box-pack: space-around;
box-pack: space-around;
align-items: center;
-webkit-align-items: center;
box-align: center;
-moz-box-align: center;
-webkit-box-align: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-moz-box-orient: vertical;
-moz-box-direction: normal;
flex-direction: column;
-webkit-flex-direction: column;
}
view{
box-sizing: border-box;
}
.my-bar{
position: fixed;
bottom: 0;
left: 0;
z-index: 9;
width: 100%;
background-color: rgba(254, 254, 254, .96);
box-shadow: 0 0 16px rgba(155, 155, 155, .5);
}
.my-bar__item{
flex: 1;
height: 98rpx;
padding-top: 10rpx;
}
.my-bar__item-text{
height: 100%;
text-align: center;
width: 100%;
color: #333;
position: relative;
}
.my-bar__item-text2:last-child{
height: 100%;
text-align: center;
width: 100%;
color: #333;
}
.my-bar__item-text, .my-bar__item-text2{
font-size: 22rpx;
}
.my-bar__item-tag{
position: absolute;
top: 0;
right: 62rpx;
width: 26rpx;
height: 26rpx;
background-color: #d01716;
color: #fff;
border-radius: 50%;
font-size: 20rpx;
}
.iconfont{
font-size: 46rpx;
}
.my-bar__item-active{
color: #d01716 !important;
}
.hackIPX{
box-sizing: content-box;
padding-bottom: 68rpx;
}
.my-bar__btn-img{
width: 50rpx;
height: 50rpx;
}
.my-bar__btn-img-act{
width: 60rpx;
height: 60rpx;
}
.hackIPX{
box-sizing: content-box;
padding-bottom: 48rpx;
padding-top: 20rpx;
}
- app.json
"usingComponents": {
"nav": "/components/nav_bar/nav_bar"
},
"tabBar": {
"custom": true,
"color": "#212121",
"selectedColor": "#d01716",
"backgroundColor": "#fefefe",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "/images/icon_index.png",
"selectedIconPath": "/images/icon_index_act.png",
"text": "首页"
},
{
"pagePath": "pages/classificationII/classificationII",
"iconPath": "/images/icon_classify.png",
"selectedIconPath": "/images/icon_classify_act.png",
"text": "分类"
},
{
"pagePath": "pages/center2/center2",
"iconPath": "/images/icon_my.png",
"selectedIconPath": "/images/icon_my_act.png",
"text": "我的"
}
]
},
- 在用到tabBar的页面.js
/**
* 生命周期函数--监听页面显示
*/
onShow: function() {
let _this = this;
if (!app.globalData.accessToken){
// Tips: 1、没有有登录状态: 只设置tabbar
console.log('没有有登录状态: 只设置tabbar')
if (typeof this.getTabBar === 'function' &&
this.getTabBar()) {
this.getTabBar().setData({
['selected']: 0
})
}
}else{
// Tips: 2、有登录状态: 拿购物车 用户消息 优惠券数据
console.log('有登录状态: 拿购物车 用户消息 优惠券数据')
app.myRequest('cartNum', {}, 'get',
function (res) {
if (typeof _this.getTabBar === 'function' &&
_this.getTabBar()) {
_this.getTabBar().setData({
['selected']: 0,
['list[2].tagNum']: res.data
})
}
_this.setData({
cartNums: res.data
})
}, 'top');
this.showCoupons();
}
},
// 其实核心代码 其他的代码跟我的业务逻辑挂钩的
//if (typeof this.getTabBar === 'function' &&
// this.getTabBar()) {
// this.getTabBar().setData({
// ['selected']: 0
// })
//}
OK 以上的这些玩意儿就够你玩转小程序的自定义tabBar了
但是我们要考虑到有些用户死活不升级的情况
(别问我为什么,在遍地都是iOS12.x 的时候,我的用户还有iOS8.x 的)
就是微信版本低, 小程序的基础库版本也低,不支持展示小程序的自定义tabBar;
此时如果我们不做兼容的话 用户将看不到底部栏
适配低版本
- 注意, 我们这里用的是自定义组件来代替小程序的自定义tabBar
从项目结构上来说是用/components/tab_bar 来代替 /custom-tab-bar
由于用到了自定义组件 而支自定义组件是从1.6.3 的基础版本开始支持的
如果需要稳定的话, 推荐线上最低基础库需要支持到 2.1.0
关键点有3个
- 自己的tabBar组件
- 隐藏官方的tabBar
- 控制自己的tabBar组建的显示和隐藏
1. 自己的tanBar组件
a. /components/tab_bar/tab_bar.js
// components/table_bar/table_bar.js
let app = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
now: {
type: String,
value: 'index'
},
cartNum: {
type: Number,
value: 0,
}
},
/**
* 组件的初始数据
*/
data: {
isIpx: app.globalData.isIpx,
},
/**
* 组件的方法列表
*/
methods: {
_tableJump: function(e) {
let jumpType = e.currentTarget.dataset.jump;
if (jumpType == this.properties.now) {
return;
} else {
if (jumpType == 'index') {
wx.switchTab({
url: '/pages/index/index'
})
} else if (jumpType == 'my') {
wx.switchTab({
url: '/pages/center2/center2'
})
} else if (jumpType == 'classify') {
wx.switchTab({
url: '/pages/classificationII/classificationII'
})
} else if (jumpType == 'cart') {
wx.navigateTo({
url: '/pages/shoppingCart/shoppingCart'
})
} else {
wx.reLaunch({
url: '/pages/find/find'
})
}
}
},
}
})
b. /components/tab_bar/tab_bar.wxml
<!--components/tab_bar/tab_bar.wxml-->
<view class='my-bar ak-flexB {{isIpx ? "hackIPX" : ""}}'>
<view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='index'>
<view class='my-bar__item-text ak-flex-columnC {{now == "index" ? "my-bar__item-active" : ""}}'>
<image src='/images/icon_index.png' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'index'}}"></image>
<image src='/images/icon_index_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'index'}}"></image>
首页
</view>
</view>
<view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='classify'>
<view class='my-bar__item-text ak-flex-columnC {{now == "classify" ? "my-bar__item-active" : ""}}'>
<image src='/images/icon_classify.png' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'classify'}}"></image>
<image src='/images/icon_classify_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'classify'}}"></image>
分类
</view>
</view>
<view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='cart'>
<view class='my-bar__item-text ak-flex-columnC {{now == "cart" ? "my-bar__item-active" : ""}}'>
<image src='/images/icon_cart.png' style='width: 56rpx; margin-top: -4rpx;' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'cart'}}"></image>
<image src='/images/icon_cart_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'cart'}}"></image>
购物车
<view hidden="{{cartNum <= 0}}" class='my-bar__item-tag ak-flexC'>{{cartNum}}</view>
</view>
</view>
<view class='my-bar__item ak-flexC' bindtap='_tableJump' data-jump='my'>
<view class='my-bar__item-text2 ak-flex-columnC {{now == "my" ? "my-bar__item-active" : ""}}'>
<image src='/images/icon_my.png' class='my-bar__btn-img animated' mode='widthFix' hidden="{{now == 'my'}}"></image>
<image src='/images/icon_my_act.png' class='my-bar__btn-img-act animated bounceIn' mode='widthFix' hidden="{{now != 'my'}}"></image>
我的
</view>
</view>
</view>
c. 在使用的页面:
pages/index/index.wxml
<!-- pages/index/index.wxml -->
<tab-bar wx:if="{{useMyTB}}" now="index" cart-num="{{cartNums}}"></tab-bar>
pages/index/index.js :
Page({
data: {
useMyTB: app.globalData.useMyTB,
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function() {
let _this = this;
if (!app.globalData.accessToken){
// Tips: 1、没有有登录状态: 只设置tabbar
console.log('没有有登录状态: 只设置tabbar')
if (typeof this.getTabBar === 'function' &&
this.getTabBar()) {
this.getTabBar().setData({
['selected']: 0
})
}
}else{
// Tips: 2、有登录状态: 拿购物车 用户消息 优惠券数据
console.log('有登录状态: 拿购物车 用户消息 优惠券数据')
app.myRequest('cartNum', {}, 'get',
function (res) {
if (typeof _this.getTabBar === 'function' &&
_this.getTabBar()) {
_this.getTabBar().setData({
['selected']: 0,
['list[2].tagNum']: res.data
})
}
_this.setData({
cartNums: res.data
})
}, 'top');
app.myRequest('centerInfo', {},
'get',
function (res) {
_this.setData({
msg_num: res.data.msg_num,
infoData: res.data,
hiddenBindPhoneBlock: res.data.member.phone == '' ? false : true
})
}, 'top');
this.showCoupons();
}
},
2. 隐藏官方的tabBar
首先我们要判断版本,如果低于2.5.0的话,
做兼容, 隐藏官方的tabBar用自定义的tabBar;
在app.js的onLaunch中检查版本做适配
// 做适配
wx.getSystemInfo({
success: function (res) {
// 判断SDK版本
let sdkv = res.SDKVersion;
console.log('当前版本: ' + sdkv)
let basicsVersion = [2, 5, 0]
sdkv = sdkv.split(".");
for (let i in sdkv) {
if (parseInt(basicsVersion[i]) > parseInt(sdkv[i])) {
console.warn('当前版本小于2.5.0')
wx.hideTabBar();
_this.globalData.useMyTB = true;
}
}
},
})
3. 控制自己的tabBar组建的显示和隐藏
其实上面代码有写啦 搜索 useMyTB
以上这些就是我对小程序自定义tabBar的理解和应用了, 欢迎指点