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%)); }
  }
}
相关推荐
卸任11 小时前
Vibe coding工具使用心得
前端·ai编程
brien912 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance12 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M12 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写12 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李12 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易13 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马13 小时前
前沿视界
前端
kyriewen13 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易13 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员