Vue实现ant-design Tabs 组件的蓝条动画

2,213 阅读2分钟

一、效果图


切换 Tabs 标签时,蓝条长度和位置有过渡效果

二、实现思路

    <g-tabs :selected-tab.sync="selectedTab" direction="column">
      <g-tabs-nav>
        <template v-slot:items>
          <g-tabs-items tagName="vehicle">vehicle</g-tabs-items>
          <g-tabs-items tagName="girl">girl</g-tabs-items>
          <g-tabs-items tagName="sports">sports</g-tabs-items>
        </template>
      </g-tabs-nav>
    </g-tabs>

切换时需要拿到 items 的宽度和位置,这样的话就必须通过传递被选中 items 实例来获取。用到的API:

  1. Element.clientWidth属性返回元素节点的 CSS 宽度,只对块级元素有效,也是只包括元素本身的宽度和padding
  2. Element.offsetLeft返回当前元素左上角相对于Element.offsetParent节点的水平位移
  3. Element.offsetParent属性返回最靠近当前元素的、并且 CSS 的position属性不等于static的上层元素。

蓝条代码有以下几种选择

  1. 写在g-tabs-nav
  2. 写在g-tabs-items
  3. 单独一个组件

无论写在哪里重点就在于拿到被选中 items 的实例,如果直接把核心代码写在 nav 里,那么必须借助eventBus,如果写在 g-tabs-items 里很难实现左右移动的效果但是宽度的动画很容易实现

三、实现代码

那么我们选择单独的一个组件,用 props 来接受 items 实例,相性很好

//g-tabs-bar.vue

<template>
  <div class="tab-bar" :style="barStyle"></div>
</template>
<script>
export default {
  name: "yibo-tab-bar",
  props: ["tabs"],
  computed: {
    barStyle: {
      get() {
        let barWidth = 0;
        let style = {};
        if (this.tabs && this.tabs.active) {
          barWidth = this.tabs.$el.clientWidth;
          let transformAfter = this.tabs.$el.offsetLeft;
          style.width = barWidth + "px";
          style.left = transformAfter + "px";
        }
        return style;
      }
    }
  }
};
</script>

<style lang="scss" scoped>
$blue: #1890ff;
.tab-bar {
  border-bottom: 1px solid $blue;
  position: absolute;
  bottom: 0;
  left: 0;
  transition: left 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
</style>

关键就在于 props: ["tabs"] 怎么传进来

//g-tabs-items.vue 

 watch:{
    active:function(val){
      if (val) {
        this.eventBus.$emit("activeVm", this);
      }
    }
  }

//g-tabs-nav.vue  

 <g-tab-bar :tabs="activeVm" ></g-tab-bar>

 data() {
    return {
      activeVm: null
    };
  },
  components: {
    "g-tab-bar": tabsBar
  },
  mounted() {
    this.eventBus.$on("activeVm", this.passingThis);
  },
  methods: {
    passingThis(activeVm) {
      this.activeVm = activeVm;
    }
  }

实现了好久咋写出来就这么点内容呢