TabBar 的实现功能
TabBar实现思路
- 如果在下方有一个单独的Tabbar组件,你如何封装
- 自定义TabBar组件,在APP中使用
- 让TabBar出于底部,并且设置相关的样式
- TabBar中显示的内容有外界决定
- 定义插槽
- flex布局平分TabBar
- 自定义TabBarItem,可以传入图片和文字
- 定义TabBarItem,并且定义两个插槽:图片、文字。
- 给两个插槽外层包装div,用于设置样式。
- 填充插槽,实现底部TabBar的效果
- 传入高亮图片
- 定义另外一个插槽,插入active-icon的数据
- 定义一个变量isActive,通过v-show来决定是否显示对应的icon
- TabBarItem绑定路由数据
- 安装路由:npm install vue-router —save
- 完成router/index.js的内容,以及创建对应的组件
- main.js中注册router
- APP中加入
<router-view>
组件
- 点击Item跳转对应路由,并动态决定isActive
- 监听item的点击,通过
this.$router.replace()
替换路由路径 - 通过
this.$route.path.indexOf(this.link) !== -1
来判断是否是active
- 监听item的点击,通过
- 动态计算active样式并且不同的分类显示不同的颜色
- 封装新的计算属性:
this.isActive ? {'color': 'red'} : {}
- 封装新的计算属性:
- 动态添加数据并进行切换
- 将数据定义到data中tabList数组中[{label:’’,itemIcon:",itemIconActive:"",path:"",activeColor:’’}]
具体实现
1.新建项目
vue create demo
2.新建目录![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6ed2f1066f7e4051861a558261437781.png#pic_center)
base.css
body{
margin: 0;
padding: 0;
}
在App.vue文件中,我们在<style>
里@import "./common/style/base.css";
来引入css样式
在main.js中用require('./common/style/base.css')
引入
3.TabBar页面搭建
App.vue代码
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">
<img src="./common/img/tabbar/home.svg" alt="">
<div>首页</div>
</div>
<div class="tab-bar-item">
<img src="./common/img/tabbar/category.svg" alt="">
<div>分类</div>
</div>
<div class="tab-bar-item">
<img src="./common/img/tabbar/shopcart.svg" alt="">
<div>购物车</div>
</div>
<div class="tab-bar-item">
<img src="./common/img/tabbar/profile.svg" alt="">
<div>我的</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
@import url("~@/common/style/base.css");
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, 100);
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
/*去掉图片下面多出来的3像素*/
vertical-align: middle;
}
</style>
4.拆分成TabBar和TabBarItem并进行封装
tabbar.vue
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
</script>
<style>
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, 100);
}
</style>
tabbarItem.vue
<template>
<router-link class="tab-bar-item" :tag="tags" :to="path">
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
</router-link>
</template>
<script>
export default {
props: {
path: {
type: String,
default: "",
},
tags:{
type:String,
default:"div"
}
},
}
</script>
<style>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
/*去掉图片下面多出来的3像素*/
vertical-align: middle;
}
</style>
app.vue
<template>
<div id="app">
<tabBar>
<tabBarItem path="/home" >
<img slot="item-icon" src="~@/common/img/tabbar/home.svg" alt="" />
<div slot="item-text">首页</div>
</tabBarItem>
<tabBarItem path="/category" >
<img slot="item-icon" src="~@/common/img/tabbar/category.svg" alt="" />
<div slot="item-text">分类</div>
</tabBarItem>
<tabBarItem path="/cart">
<img slot="item-icon" src="~@/common/img/tabbar/shopcart.svg" alt="" />
<div slot="item-text">购物车</div>
</tabBarItem>
<tabBarItem path="/my">
<img slot="item-icon" src="~@/common/img/tabbar/profile.svg" alt="" />
<div slot="item-text">我的</div>
</tabBarItem>
</tabBar>
</div>
</template>
<script>
import tabBar from '@/page/tabbar/tabbar.vue'
import tabBarItem from '@/page/tabbar/TabBarItem.vue';
export default {
name: 'App',
components: {
tabBar,
tabBarItem
}
}
</script>
<style>
@import url("~@/common/style/base.css");
</style>
4.路由跳转
-
安装路由:npm install vue-router
-
使用
-
创建router
-
index.js
//配置路由相关信息 import Vue from 'vue' import VueRouter from 'vue-router'; // 通过vue.use(路由插件),安装插件 Vue.use(VueRouter) // 路由懒加载 const home = () => import("@/page/home/index.vue") const category = () => import("@/page/category/index.vue") const cart = () => import("@/page/cart/index.vue") const my = () => import("@/page/my/index.vue") // 定义路由 let routes = [{ path: "", redireact: '/home' }, { path: "/home", name: "首页", component: home }, { path: "/category", name: "分类", component: category, }, { path: "/cart", name: "购物车", component: cart }, { path: "/my", component: my, name: "我的" }, ] // 创建router实例 let router = new VueRouter({ // 配置路由和组件之间的应用关系 mode: "history", //abstract || history || hash= routes }) // 导出router实例 export default router
-
-
在main引入,并重新启动项目
import router from './router/index.js';
-
app.js
<template> <div id="app"> <router-view></router-view> <mainTab></mainTab> </div> </template> <script> import mainTab from '@/page/mainTable/index.vue' export default { name: 'App', components: { mainTab } } </script> <style> @import url("~@/common/style/base.css"); </style>
-
tabbarItem.vue发生改变
<template> <router-link class="tab-bar-item" :tag="tags" :to="path"> <slot name="item-icon"></slot> <slot name="item-text"></slot> </router-link> </template> <script> export default { props: { path: { type: String, default: "", }, tags:{ type:String, default:"div" } }, } </script> <style> .tab-bar-item { flex: 1; text-align: center; height: 49px; } .tab-bar-item img { width: 24px; height: 24px; } .tab-bar-item { flex: 1; text-align: center; height: 49px; font-size: 14px; } .tab-bar-item img { width: 24px; height: 24px; margin-top: 3px; /*去掉图片下面多出来的3像素*/ vertical-align: middle; } </style>
-
5.点击Item跳转对应路由,并动态决定isActive
tabbarItem.vue
<template>
<router-link class="tab-bar-item" :tag="tags" :to="path">
<div v-if="isActive">
<slot name="item-icon-active"></slot>
</div>
<div v-if='!isActive'>
<slot name="item-icon"></slot>
</div>
<slot name="item-text"></slot>
</router-link>
</template>
<script>
export default {
props: {
path: {
type: String,
default: "",
},
tags: {
type: String,
default: "div"
},
},
computed: {
isActive() {
return this.$route.path.indexOf(this.path) !== -1;
},
},
}
</script>
<style>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
/*去掉图片下面多出来的3像素*/
vertical-align: middle;
}
</style>
mainTable.vue
<template>
<div id="app">
<tabBar>
<tabBarItem path="/home" >
<img
slot="item-icon-active"
src="~@/common/img/tabbar/home_active.svg"
alt=""
/>
<img slot="item-icon" src="~@/common/img/tabbar/home.svg" alt="" />
<div slot="item-text">首页</div>
</tabBarItem>
<tabBarItem path="/category" >
<img
slot="item-icon-active"
src="~@/common/img/tabbar/category_active.svg"
alt=""
/>
<img slot="item-icon" src="~@/common/img/tabbar/category.svg" alt="" />
<div slot="item-text">分类</div>
</tabBarItem>
<tabBarItem path="/cart">
<img
slot="item-icon-active"
src="~@/common/img/tabbar/shopcart_active.svg"
alt=""
/>
<img slot="item-icon" src="~@/common/img/tabbar/shopcart.svg" alt="" />
<div slot="item-text">购物车</div>
</tabBarItem>
<tabBarItem path="/my">
<img
slot="item-icon-active"
src="~@/common/img/tabbar/profile_active.svg"
alt=""
/>
<img slot="item-icon" src="~@/common/img/tabbar/profile.svg" alt="" />
<div slot="item-text">我的</div>
</tabBarItem>
</tabBar>
</div>
</template>
<script>
import tabBar from '@/page/tabbar/tabbar.vue'
import tabBarItem from '@/page/tabbar/TabBarItem.vue';
export default {
components: {
tabBar,
tabBarItem
}
}
</script>
<style>
</style>
5.动态计算active样式并且不同的分类显示不同颜色
<template>
<router-link class="tab-bar-item" :tag="tags" :to="path">
<div v-if="isActive">
<slot name="item-icon-active"></slot>
</div>
<div v-if='!isActive'>
<slot name="item-icon"></slot>
</div>
<div :style="activeStyle">
<slot name="item-text"></slot>
</div>
</router-link>
</template>
<script>
export default {
props: {
path: {
type: String,
default: "",
},
tags: {
type: String,
default: "div"
},
activeColor:{
type:String,
default:''
}
},
computed: {
isActive() {
return this.$route.path.indexOf(this.path) !== -1;
},
activeStyle(){
return this.isActive? {'color': this.activeColor} : {}
}
},
}
</script>
<style>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
/*去掉图片下面多出来的3像素*/
vertical-align: middle;
}
</style>
mainTable.vue
<tabBarItem path="/home" activeColor="red">
<img slot="item-icon" src="~@/assets/img/tabbar/home.svg" alt="" />
<img
slot="item-icon-active"
src="~@/assets/img/tabbar/home_active.svg"
alt=""
/>
<div slot="item-text">首页</div>
</tabBarItem>
<tabBarItem path="/category" activeColor="green">
<img slot="item-icon" src="~@/assets/img/tabbar/category.svg" alt="" />
<img
slot="item-icon-active"
src="~@/assets/img/tabbar/category_active.svg"
alt=""
/>
<div slot="item-text">分类</div>
</tabBarItem>
<tabBarItem path="/cart" activeColor="yellow">
<img slot="item-icon" src="~@/assets/img/tabbar/shopcart.svg" alt="" />
<img
slot="item-icon-active"
src="~@/assets/img/tabbar/shopcart_active.svg"
alt=""
/>
<div slot="item-text">购物车</div>
</tabBarItem>
<tabBarItem path="/my" activeColor="blue">
<img slot="item-icon" src="~@/assets/img/tabbar/profile.svg" alt="" />
<img
slot="item-icon-active"
src="~@/assets/img/tabbar/profile_active.svg"
alt=""
/>
<div slot="item-text">我的</div>
</tabBarItem>
</tabbar>
6.动态添加数据并进行切换
<template>
<div id="app">
<tabBar>
<tabBarItem v-for="(item,index) in tabList" :key="index" :path="item.path" :activeColor='item.activeColor'>
<img slot="item-icon-active" :src="item.itemIconActive" alt="" />
<img slot="item-icon" :src="item.itemIcon" alt="" />
<div slot="item-text">{{item.label}}</div>
</tabBarItem>
</tabBar>
</div>
</template>
<script>
import tabBar from '@/page/tabbar/tabbar.vue'
import tabBarItem from '@/page/tabbar/TabBarItem.vue';
export default {
components: {
tabBar,
tabBarItem
},
data() {
return {
tabList: [{
path: "/home",
activeColor: "red",
itemIcon: require("@/common/img/tabbar/home.svg"),
itemIconActive: require("@/common/img/tabbar/home_active.svg"),
label: "首页"
},
{
path: "/category",
activeColor: "yellow",
itemIcon: require("@/common/img/tabbar/category.svg"),
itemIconActive: require("@/common/img/tabbar/category_active.svg"),
label: "分类"
},
{
path: "/cart",
activeColor: "blue",
itemIcon: require("@/common/img/tabbar/shopcart.svg"),
itemIconActive: require("@/common/img/tabbar/shopcart_active.svg"),
label: "购物车"
},
{
path: "/my",
activeColor: "green",
itemIcon: require("@/common/img/tabbar/profile.svg"),
itemIconActive: require("@/common/img/tabbar/profile_active.svg"),
label: "我的"
},
]
}
}
}
</script>
<style>
</style>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3V24mBB3-1647787006890)(E:\资料存档\资料笔记\总结项目\img\image-20220320223346570.png)]
activeColor: "yellow",
itemIcon: require("@/common/img/tabbar/category.svg"),
itemIconActive: require("@/common/img/tabbar/category_active.svg"),
label: "分类"
},
{
path: "/cart",
activeColor: "blue",
itemIcon: require("@/common/img/tabbar/shopcart.svg"),
itemIconActive: require("@/common/img/tabbar/shopcart_active.svg"),
label: "购物车"
},
{
path: "/my",
activeColor: "green",
itemIcon: require("@/common/img/tabbar/profile.svg"),
itemIconActive: require("@/common/img/tabbar/profile_active.svg"),
label: "我的"
},
]
}
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/7574d5cdf6984a3c8fad2180f4d8d9ef.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/b46f5bb2ce8e4df88a3981216f77adc6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56iL5bqP5r2H5r2H,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)