在前端开发中,TabBar 是一个非常常见的 UI 组件,而在一些设计需求中,我们希望激活的 Tab 有动态变化的圆角效果,并且宽度可以根据选中状态自适应。本文将分享一个 Vue3 + Less 实现的 TabBar 组件案例,完整分析代码结构、实现思路,并展示优化技巧。
效果预览:



🧩 需求分析
我们希望 TabBar 拥有以下特性:
- 动态激活状态 :根据
modelValue高亮当前 Tab。 - 圆角过渡效果:激活 Tab 的上下圆角动态显示。
- 自适应宽度:选中 Tab 宽度放大,未选中 Tab 等分。
- 复用性强:多种激活形态(左圆角、右圆角、左右圆角)可复用样式。
- 响应用户点击:选中状态更新并触发事件。
🛠 组件实现
1️⃣ 模板部分
vue
<template>
<div class="tabbar" :style="{ 'grid-template-columns': getGridFr }">
<div
v-for="(it) in tabs"
:key="it.value"
:class="{
'tabbar-item': true,
'active': modelValue === it.value && modelValue === 'use',
'active1': modelValue === it.value && modelValue === 'charge',
'active2': modelValue === it.value && modelValue === 'code',
}"
@click="select(it.value)"
>
<div class="top"></div>
{{ it.label }}
</div>
</div>
</template>
- 动态类绑定 :根据不同 Tab 值,使用
active1/active2/active3控制圆角样式。 - 网格布局 :
grid-template-columns根据选中状态动态调整宽度。
样式部分(Less )
less
.tabbar {
display: grid;
transition: all 0.3s;
--th: 8px;
&-item {
text-align: center;
height: 40px;
background-color: var(--el-color-primary);
color: #fff;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
}
// ----------- 公共激活样式 -----------
.active-base() {
color: var(--el-color-primary);
background-color: #fff;
position: relative;
--bh: 40px;
.top {
position: absolute;
height: var(--th);
background-color: #fff;
top: calc(var(--th) * -1);
}
}
// 圆角片段(左右通用)
.circle(@pos, @clip) {
display: block;
content: '';
position: absolute;
width: var(--bh);
height: var(--bh);
background-color: var(--el-color-primary);
@{pos}: 0;
bottom: 0;
z-index: 1;
clip-path: @clip;
}
// ----------- 各种激活样式 -----------
// 左右都有圆角
.active {
.active-base();
.top {
border-radius: 100px 100px 20px 20px;
width: calc(100% - 80px);
}
&::before { .circle(left, ellipse(100% 100% at 0% 0%)); }
&::after { .circle(right, ellipse(100% 100% at 100% 0%)); }
}
// 右侧圆角
.active1 {
.active-base();
.top {
border-radius: 0 100px 20px 20px;
width: calc(100% - 40px);
left: 0;
}
&::after { .circle(right, ellipse(100% 100% at 100% 0%)); }
}
// 左侧圆角
.active2 {
.active-base();
.top {
border-radius: 100px 0 20px 20px;
width: calc(100% - 40px);
right: 0;
}
&::before { .circle(left, ellipse(100% 100% at 0% 0%)); }
}
}