vue之封装Tab栏

552 阅读1分钟

涉及的知识点:

  • 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"

最终效果:

1.jpg

2.jpg

3.jpg