涉及的知识点:
- computed--计算属性
- slot--插槽
- 动画
- ...
难点: 动态计算Tab栏下划线的宽度以及偏移量
实现代码:
components/tab.vue----子组件
<template>
<div>
<ul class="tab-menu">
<li
v-for="(tab, index) in tabs"
:key="index"
@click="selectTab(index)"
:class="{ active: activeTab === index }"
>
{{ tab }}
</li>
<div
:class="{ 'underline-animate': animate }"
:style="underlineStyles"
></div>
</ul>
<div class="tab-content">
<div v-show="activeTab === 0">
<slot name="tab-content1"></slot>
</div>
<div v-show="activeTab === 1">
<slot name="tab-content2"></slot>
</div>
<div v-show="activeTab === 2">
<slot name="tab-content3"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
activeTab: 0,
animate: false,
tabs: ["Tab1", "Tab2", "Tab3"],
};
},
computed: {
/**
* 动态计算下划线的样式
* 根据当前选中的 Tab 索引以及 Tab 的数量来计算下划线的宽度和偏移量
*/
underlineStyles() {
// 下划线宽度
const activeTabWidth = 100 / this.tabs.length;
// 下划线偏移量
const activeTabOffset = activeTabWidth * this.activeTab + "%";
return {
width: activeTabWidth + "%",
left: activeTabOffset,
};
},
},
methods: {
// 监听tab栏切换
selectTab(index) {
this.animate = true;
this.activeTab = index;
this.animate = false;
},
},
};
</script>
<style scoped>
* {
padding: 0;
margin: 0;
}
ul,
li {
list-style: none;
}
.tab-menu {
display: flex;
justify-content: space-around;
padding: 10px;
border-bottom: 1px solid #ccc;
position: relative;
}
.tab-menu ul {
list-style-type: none;
display: flex;
margin: 0;
padding: 0;
}
.tab-menu li {
cursor: pointer;
padding: 10px;
}
.tab-menu li.active {
font-weight: bold;
}
.tab-menu div {
position: absolute;
bottom: 0;
height: 2px;
background-color: #000;
transition: left 0.3s, width 0.3s;
}
.underline-animate {
animation: underlineAnimation 0.3s linear;
}
@keyframes underlineAnimation {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
views/home.vue----父组件
<template>
<div>
<Tab>
<template v-slot:tab-content1> Content1 </template>
<template v-slot:tab-content2> Content2 </template>
<template v-slot:tab-content3> Content3 </template>
</Tab>
</div>
</template>
<script>
import Tab from "@/components/tab.vue";
export default {
components: {
Tab,
},
};
</script>
这里的 v-slot:tab-content1 写法也可以简写成slot="tab-content1"
最终效果: