概括
- 注意事項(xiàng)
- 項(xiàng)目結(jié)構(gòu)
- 代碼
- 適配低版本
參考文檔:
小程序官方的tabBar文檔
小程序官方的Component文檔
小程序app.json的配置
注意事項(xiàng)
- 自定義tabBar 基礎(chǔ)庫 2.5.0 開始支持
- 自定義tabBar的組件一定要叫 custom-tab-bar 并且一定要與app.js同級(jí)
- 在 app.json 中的 tabBar 項(xiàng)指定 custom 字段散庶,同時(shí)其余 tabBar 相關(guān)配置也補(bǔ)充完整欠橘。
- 所有 tab 頁的 json 里需聲明usingComponents 項(xiàng),也可以在 app.json 全局開啟。
// app.json 記得刪掉注釋
"usingComponents": {
"nav": "/components/nav_bar/nav_bar" // 用與做低版本適配的 自定義tabBar組件
},
"tabBar": {
"custom": true, // 打開tabBar的自定義功能
"color": "#212121",
...
項(xiàng)目結(jié)構(gòu)
- 代替原生tabBar的 custom-tab-bar 是不是一定要這樣放危队?
是的肴沫,一定要放在與app.js同級(jí)的項(xiàng)目結(jié)構(gòu), 而且一定要叫 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,
}
},
/**
* 組件的初始數(shù)據(jù)
*/
data: {
isIpx: app.globalData.isIpx, // 用于適配全面屏的底部高度(iPhone X* 的底部杠杠)
selected: 0, // 當(dāng)前選中的項(xiàng)
color: "#333", // 未選中的字體的顏色
selectedColor: "#d01716", // 選中時(shí)的字體顏色
list: [{
pagePath: "/pages/index/index", // 跳轉(zhuǎn)路徑掉房, 【switchTab的跳轉(zhuǎn)一定要在app.json中配置】
iconPath: "/images/icon_index.png",
selectedIconPath: "/images/icon_index_act.png",
jumpType: "switchTab", // 跳轉(zhuǎn)的類型
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: "購(gòu)物車"
}, {
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
/**
* 生命周期函數(shù)--監(jiān)聽頁面顯示
*/
onShow: function() {
let _this = this;
if (!app.globalData.accessToken){
// Tips: 1茧跋、沒有有登錄狀態(tài): 只設(shè)置tabbar
console.log('沒有有登錄狀態(tài): 只設(shè)置tabbar')
if (typeof this.getTabBar === 'function' &&
this.getTabBar()) {
this.getTabBar().setData({
['selected']: 0
})
}
}else{
// Tips: 2、有登錄狀態(tài): 拿購(gòu)物車 用戶消息 優(yōu)惠券數(shù)據(jù)
console.log('有登錄狀態(tài): 拿購(gòu)物車 用戶消息 優(yōu)惠券數(shù)據(jù)')
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();
}
},
// 其實(shí)核心代碼 其他的代碼跟我的業(yè)務(wù)邏輯掛鉤的
//if (typeof this.getTabBar === 'function' &&
// this.getTabBar()) {
// this.getTabBar().setData({
// ['selected']: 0
// })
//}
OK 以上的這些玩意兒就夠你玩轉(zhuǎn)小程序的自定義tabBar了
但是我們要考慮到有些用戶死活不升級(jí)的情況
(別問我為什么卓囚,在遍地都是iOS12.x 的時(shí)候瘾杭,我的用戶還有iOS8.x 的)
就是微信版本低, 小程序的基礎(chǔ)庫版本也低哪亿,不支持展示小程序的自定義tabBar粥烁;
此時(shí)如果我們不做兼容的話 用戶將看不到底部欄
適配低版本
- 注意, 我們這里用的是自定義組件來代替小程序的自定義tabBar
從項(xiàng)目結(jié)構(gòu)上來說是用/components/tab_bar 來代替 /custom-tab-bar
由于用到了自定義組件 而支自定義組件是從1.6.3 的基礎(chǔ)版本開始支持的
如果需要穩(wěn)定的話蝇棉, 推薦線上最低基礎(chǔ)庫需要支持到 2.1.0
關(guān)鍵點(diǎn)有3個(gè)
- 自己的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,
}
},
/**
* 組件的初始數(shù)據(jù)
*/
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>
購(gòu)物車
<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,
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面顯示
*/
onShow: function() {
let _this = this;
if (!app.globalData.accessToken){
// Tips: 1讨阻、沒有有登錄狀態(tài): 只設(shè)置tabbar
console.log('沒有有登錄狀態(tài): 只設(shè)置tabbar')
if (typeof this.getTabBar === 'function' &&
this.getTabBar()) {
this.getTabBar().setData({
['selected']: 0
})
}
}else{
// Tips: 2、有登錄狀態(tài): 拿購(gòu)物車 用戶消息 優(yōu)惠券數(shù)據(jù)
console.log('有登錄狀態(tài): 拿購(gòu)物車 用戶消息 優(yōu)惠券數(shù)據(jù)')
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('當(dāng)前版本: ' + sdkv)
let basicsVersion = [2, 5, 0]
sdkv = sdkv.split(".");
for (let i in sdkv) {
if (parseInt(basicsVersion[i]) > parseInt(sdkv[i])) {
console.warn('當(dāng)前版本小于2.5.0')
wx.hideTabBar();
_this.globalData.useMyTB = true;
}
}
},
})
3. 控制自己的tabBar組建的顯示和隱藏
其實(shí)上面代碼有寫啦 搜索 useMyTB
以上這些就是我對(duì)小程序自定義tabBar的理解和應(yīng)用了奇瘦, 歡迎指點(diǎn)