uniapp自定义tabbar(支持中间凸起,角标,全端适用)
背景
在使用uniapp进行开发时,tabbar是我们使用的很频繁的一个组件,但是在特定的平台会有一些使用上的限制,无法通过一套代码来做通用平台的适配。比如说中间按钮凸起,动态隐藏某个tab(不同角色展示不同功能),使用字体图标,数字角标等,这些功能不是所有平台都支持。今天我们就用一套代码来实现这些功能全端支持。
思路
实现思路就是通过通过自定义view来实现我们这个tabbar功能,然后页面通过组件来展示。通过点击不同的tab来显示不同的组件来达到模拟原生tabbar切换效果。那有些人要问了,你咋知道我项目中有多少个tab,这些tab叫什么名字了?那这里就可以利用uniapp提供的组件easycom模式来解决这些问题,只要我们设置好组件的限制个数和提前占位名称,这些问题就迎刃而解。
实现
1、我们现在components(没有就新建一个components目录)目录下新建一个文件夹(我这里叫ctab),然后分别新建一个vue组件和一个js文件。组件可以让我们在其他地方引用,js文件主要是做配置。
2、新建tab组件,我们组件最多限制5个tab组件,然后需要通过easycom占位来实现,所以你需要几个tab组件就在components目录下建几个组件命名为ctabx。如下所示,我这里要展示三个tab:
特别注意这里的tab组件命名一定要符合easycom规范,不然可能会引起组件引用错误。
这里示例一个ctab1写法:
<template><view style="width: 750rpx;height: 300rpx;background-color: red;">
首页
</view></template><script>exportdefault{name:"ctab1",data(){return{};},mounted(){},methods:{}}</script><style></style>
3、tabbar组件ctab.vue实现,这里就直接上代码了,直接copy就能使用,关键地方已加上注释
<template><view><!--中间按钮凸起模式--><block v-if="midBtn && midBtn.show"><!--凸起模式最多展示四个--><block v-if="tabs.length < 4"><ctab1 v-show="sindex == 0"></ctab1><ctab2 v-show="sindex == 2"></ctab2></block><block v-else="tabs.length >= 4"><ctab1 v-show="sindex == 0"></ctab1><ctab2 v-show="sindex == 1"></ctab2><ctab3 v-show="sindex == 3"></ctab3><ctab4 v-show="sindex == 4"></ctab4></block><view class="tabbar"><!--中间按钮凸起模式tab为3个或者5个--><view class="tab-item"
v-for="(item,index) in (tabs.length < 4 ? 3 : 5)":key="item"
@click="handleTabClick(index)"><!--中间按钮凸起显示图片和文字--><block v-if="index == floor"><view :style="'bottom: calc('+(midBtn.offset ? midBtn.offset : '50rpx')+' + env(safe-area-inset-bottom));width: '+(midBtn.width ? midBtn.width : '150rpx')+';height: '+(midBtn.width ? midBtn.width : '150rpx')+';background:'+(midBtn.background ? midBtn.background : '#ffffff')"class="mid-btn"><image :src="midBtn.icon":style="'width: '+(midBtn.iconwidth ? midBtn.iconwidth : midBtn.width)+';height: '+(midBtn.iconheight ? midBtn.iconheight : midBtn.width)+';'"
@click="handleMidBtn"/></view><text class="mid-text":style="'margin-top: '+(midBtn.textoffset ? midBtn.textoffset : '50rpx;')"
v-show="midBtn.showtext">{{midBtn.text}}</text></block><!--普通tab这里需要注意index选择--><block v-else><view class="c-tab-item"><text :class="'tab-iconfont iconfont '+(tabs[index < floor ? index : index-1].iconfont)":style="'color:'+(sindex == index ? scolor : color)"
v-if="tabs[index < floor ? index : index-1].iconfont"/><image :src="sindex == index ? tabs[index < floor ? index : index-1].iconSelect : tabs[index < floor ? index : index-1].icon"class="tab-icon"
v-else/><text class="tab-text":style="'color:'+(sindex == index ? scolor : color)">{{tabs[index < floor ? index : index-1].text}}</text><view class="corner"
v-show="tabs[index < floor ? index : index-1].mark > 0">{{tabs[index < floor ? index : index-1].mark >99?'99+': tabs[index < floor ? index : index-1].mark}}</view></view></block></view></view></block><!--普通模式--><block v-else><block v-if="tabs.length == 1"><ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1></block><block v-else-if="tabs.length == 2"><ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1><ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2></block><block v-else-if="tabs.length == 3"><ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1><ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2><ctab3 v-show="sindex == 2 && tabs[2].show"></ctab3></block><block v-else-if="tabs.length == 4"><ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1><ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2><ctab3 v-show="sindex == 2 && tabs[2].show"></ctab3><ctab4 v-show="sindex == 3 && tabs[3].show"></ctab4></block><block v-else-if="tabs.length >= 5"><ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1><ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2><ctab3 v-show="sindex == 2 && tabs[2].show"></ctab3><ctab4 v-show="sindex == 3 && tabs[3].show"></ctab4><ctab5 v-show="sindex == 4 && tabs[4].show"></ctab5></block><view class="tabbar"><view class="tab-item"
v-for="(item,index) in tabs":key="item.text"
v-show="item.show"
@click="handleTabClick(index)"><view class="c-tab-item"><text :class="'tab-iconfont iconfont '+(item.iconfont)":style="'color:'+(sindex == index ? scolor : color)"
v-if="item.iconfont"/><image :src="sindex == index ? item.iconSelect : item.icon"class="tab-icon"
v-else/><text class="tab-text":style="'color:'+(sindex == index ? scolor : color)">{{item.text}}</text><view class="corner"
v-show="item.mark > 0">{{item.mark >99?'99+': item.mark}}</view></view></view></view></block></view></template><script>//读取配置import ctabbar from'./ctab-config.js'exportdefault{name:"ctab",data(){return{tabs:[],color:'',scolor:'',midBtn:{},sindex:0,floor:-1,//midButton开启时使用}},mounted(){let tabbar = ctabbar.tabbar
this.color = tabbar.color
this.scolor = tabbar.selectcolor
if(tabbar.midButton && tabbar.midButton.show && tabbar.tabs.length <2){thrownewError('midButton模式开启,配置tab选项不能少于2个')}if(tabbar.midButton && tabbar.midButton.show){let mlength = tabbar.tabs.length <4?3:5this.floor = Math.floor(mlength/2)}//普通模式,设置选中的tab项let tablen = tabbar.tabs.length
if(!tabbar.midButton.show){if(!tabbar.tabs[0].show){this.sindex ++if(tablen >=2&&!tabbar.tabs[1].show){this.sindex ++if(tablen >=3&&!tabbar.tabs[2].show){this.sindex ++if(tablen >=4&&!tabbar.tabs[3].show){this.sindex ++if(tablen >=5&&!tabbar.tabs[4].show){thrownewError('tab不能全部隐藏')}}}}}}if(tabbar.tabs.length <=5){this.tabs = tabbar.tabs
}else{this.tabs = tabbar.tabs.slice(0,5)}this.midBtn = tabbar.midButton
},methods:{setTheme(color){this.scolor = color
this.midBtn.background = color
},//设置tab隐藏和显示,midButton模式失效setTabVisible(index,visible){if(this.tabs[index]){this.tabs[index].show = visible
}},//设置角标setCorner(index,num){if(this.tabs[index]){this.tabs[index].mark = num
}},handleTabClick(tab){if(this.midBtn &&this.midBtn.show){if(tab ==this.floor){return}}this.sindex = tab
let rindex = tab
if(this.midBtn &&this.midBtn.show){if(tab >this.floor){
rindex --}}this.$emit('tabClick',rindex)},handleMidBtn(){this.$emit('midClick')}}}</script><style>/*这里引入字体图标,如果使用字体图标的话*/
@import'@/common/font/iconfont.css';.tabbar {position: fixed;
z-index:99;width:100%;height: 100rpx;
background-color: #ffffff;bottom:0;left:0;
box-shadow: 0rpx 4rpx 8rpx 0rpx rgba(0,0,0,0.5);
border-radius: 0px 0px 0px 0px;opacity:1;display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding-bottom:constant(safe-area-inset-bottom);
padding-bottom:env(safe-area-inset-bottom);
box-sizing: content-box;}.tab-item {flex:1;height: 100rpx;display: flex;
flex-direction: column;
align-items: center;
justify-content: center;}.c-tab-item {height: 120rpx;display: flex;
flex-direction: column;width: 120rpx;
align-items: center;
justify-content: center;position: relative;}.tab-icon {width: 45rpx;height: 45rpx;}.tab-iconfont {
font-size: 45rpx;
font-weight: bold;}.tab-text {
font-size: 26rpx;color: #333333;
margin-top: 5rpx;}.mid-btn {position: absolute;display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: red;
border-radius:50%;}.mid-text {
font-size: 25rpx;color: #999999;}.corner {
text-align: center;width: 45rpx;height: 45rpx;position: absolute;
background-color: red;
border-radius:50%;color: white;
font-size: 20rpx;
font-weight: bold;top: 5rpx;right:0;display: flex;
flex-direction: row;
justify-content: center;
align-items: center;}</style>
4、配置文件如下:
var tabbar ={/*开启midButton模式时取前两个或者前四个显示,其他忽略*/midButton:{show:true,//是否是中间凸起模式width:'153rpx',//不填默认150rpx 中间按钮大小iconwidth:'67rpx',//不填默认150rpx 中间图标大小iconheight:'60rpx',offset:'40rpx',//不填默认50rpxbackground:'#F7D456',//中间按钮背景颜色text:'拍一拍',textoffset:'50rpx',//不填默认50rpxshowtext:false,icon:'../../static/tabbar/camera.png'},color:'#333333',//未选中颜色selectcolor:'#F7D456',//选中颜色/*tabs最多5个,超过5个超过的部分会被忽略,show属性用来控制tab显示隐藏,midButton开启时失效,iconfont优先,没有就使用icon*/tabs:[{icon:'../../static/tabbar/main_tab_home_normal.png',iconSelect:'../../static/tabbar/main_tab_home_select.png',text:'首页',iconfont:'',show:true,mark:0//角标数量,小于等于0不显示},{icon:'../../static/tabbar/main_tab_task_normal.png',iconSelect:'../../static/tabbar/main_tab_task_select.png',text:'任务',iconfont:'',show:true,mark:100},{icon:'../../static/tabbar/main_tab_my_normal.png',iconSelect:'../../static/tabbar/main_tab_my_select.png',text:'我的',iconfont:'icon-wode',//注意配置字体图标会优先使用字体图标,这里是示例show:true,mark:9}]}
module.exports ={
tabbar
}
5、使用示例:
<template><ctab @midClick='midClick'
@tabClick='tabClick'
ref="ctab"/></template><script>exportdefault{data(){return{}},onLoad(){},methods:{//凸起按钮点击事件midClick(){
console.log('midClick')},//tab切换点击事件tabClick(tab){
console.log('tabClick',tab)}}}</script><style>
page {width:100%;height:100%;}</style>
6、到这里我们自定义tabbar就完成了,通过修改配置文件中的midButton中的show属性来开启是否中间按钮凸起,接下来我们看下效果。
midButton开启:
普通模式:
再普通模式下,我们可以通过配置或者动态修改tabs中tab obj中的show属性来动态形式和隐藏某个tab,我们这里配置第一个tab为隐藏:
...tabs:[{icon:'../../static/tabbar/main_tab_home_normal.png',iconSelect:'../../static/tabbar/main_tab_home_select.png',text:'首页',iconfont:'',show:false,//隐藏第一个tabmark:0//角标数量,小于等于0不显示},{icon:'../../static/tabbar/main_tab_task_normal.png',iconSelect:'../../static/tabbar/main_tab_task_select.png',text:'任务',iconfont:'',show:true,mark:100},{icon:'../../static/tabbar/main_tab_my_normal.png',iconSelect:'../../static/tabbar/main_tab_my_select.png',text:'我的',iconfont:'',show:true,mark:9}]...
效果图如下:
7、到这里我们的自定义tabbar就完成了,剩下的就是在tab组件中实现我们各个页面的逻辑。我们通过配置文件可以轻松的使用一个套代码实现tabbar中间按钮凸起、数字角标、动态隐藏、自定义mask覆盖tabbar(需要自己控制好层级),字体图标等功能,并且全端适用。
尾巴
本次内容就这么多,欢迎给我点赞和关注,有什么疑问欢迎给我留言,谢谢!
版权归原作者 abs625 所有, 如有侵权,请联系我们删除。