Vue3 + Less 实现动态圆角 TabBar:从代码到优化实践

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

效果预览:

🧩 需求分析

我们希望 TabBar 拥有以下特性:

  1. 动态激活状态 :根据 modelValue 高亮当前 Tab。
  2. 圆角过渡效果:激活 Tab 的上下圆角动态显示。
  3. 自适应宽度:选中 Tab 宽度放大,未选中 Tab 等分。
  4. 复用性强:多种激活形态(左圆角、右圆角、左右圆角)可复用样式。
  5. 响应用户点击:选中状态更新并触发事件。

🛠 组件实现

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%)); }
  }
}
相关推荐
安_几秒前
<style scoped>跟<style>有什么区别
前端·vue
姝然_95272 分钟前
Claude Code 命令完整文档
前端
wjcroom2 分钟前
web版进销存的设计到实现一
前端
无知的前端5 分钟前
Flutter常见问题以及解决方案
前端·flutter·dart
BD_Marathon17 分钟前
Vue3_Vite构建工程化前端项目
前端
武清伯MVP18 分钟前
CSS Grid布局全解析:从基础到实战的二维布局方案
前端·css·grid
xfq24 分钟前
typescript泛型枚举以及NaN传染处理
前端·typescript
ErMao24 分钟前
开始搭建第一个React项目吧~
前端·react.js
Yanni4Night33 分钟前
JavaScript打包器大奖赛:谁是构建速度之王? 🚀
前端·javascript
默海笑1 小时前
VUE后台管理系统:项目架构之搭建Layout架构解决方案与实现
前端·javascript·vue.js