uniapp中不到200行实现vant ui的tab组件

298 阅读1分钟

uniapp中不到200行实现vant ui的tab组件,可左右滑动屏幕切换tab,可自动显示隐藏的tab

<view class="container">
    <view class="main">
      <scroll-view class="tabs" :scroll-x="true" :scroll-left="scrollLeft" :scroll-with-animation="true" :enable-flex="true">
        <block v-for="(title, index) in tabs" :key="index">
          <view class="item" @tap="changeTab(index)" :class="{ 'active': active === index }">
            <text>{{ title }}</text>
          </view>
        </block>
        <view class="line" :style="getLineStyles">
          <text></text>
        </view>
      </scroll-view>
    </view>
    <view class="content" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
        <view class="box">
          <slot></slot>
        </view>
    </view>
  </view>
<script>
export default {
  props: {
    tabs: {
      type: Array,
      required: true
    }, //父组件传递进来的,tabTitle
    active: {
      type: Number,
      required: true
    }, //切换tab时的active值,全权交给父组件更新
    scrollLeftAct: {
      type: Number
    }, //在到达某个active的时候scroll left
    scrollRgihtAct: {
      type: Number
    } ////在到达某个active的时候scroll right
  },
  data() {
    return {
      scrollLeft: 0,
      clientWidth: 0,
      contentWidth: 0,
      startX: 0,
      currentX: 0,
      deltaX: 0
    }
  },
  created() {
    const systemInfo = uni.getSystemInfoSync();
	  this.clientWidth = systemInfo.screenWidth; 

    this.contentWidth = this.tabs.length * 20 / 100 * this.clientWidth
  },
  computed: {
    getLineStyles() {
      return {
        width: `20%`,
        left: `${this.active * 20}%`
      }
    } 
  },
  methods: {
    scrollToLeft() {  
      this.scrollLeft = 0; // 设置滚动条位置为0,即最左边  
    },
    scrollToRight() {
      this.scrollLeft = this.contentWidth - this.clientWidth; // 滚动到最右边  

      console.log(this.scrollLeft);
    },
    changeTab(index) {
      if (index === this.scrollLeftAct) {
        this.scrollToLeft();
      }

      if (index === this.scrollRgihtAct) {
        this.scrollToRight();
      }
      this.$emit('changeTab', index);
    },
    getLineMoveStyle(val, oval) {
      const diff = (val - oval) * 100;
      return {
        right: diff + '%'
      }
    },
    touchstart(event) {
      // 获取触摸开始时的 X 坐标
      this.startX = event.touches[0].clientX;
    },
    touchmove(event) {
      // 获取触摸移动时的 X 坐标
      this.currentX = event.touches[0].clientX;

      // 计算滑动的距离
      this.deltaX = this.currentX - this.startX;
    },
    touchend() {
      if (Math.abs(this.deltaX) > 30) {
        // 向右滑动
        if (this.deltaX > 0 && this.active > 0) {
          setTimeout(() => {
            this.changeTab(this.active - 1);
          });
        }
        // 向左滑动
        if (this.deltaX < 0 && this.active < this.tabs.length - 1) {
          setTimeout(() => {
            this.changeTab(this.active + 1);
          });
        }
      }
      this.deltaX = 0;
    },
  }
}
</script>
<style scoped>
.container {
  height: 100%;
  width: 100%;
}

.main {
  position: fixed;
  width: 100%;
}

.tabs {
  height: 82upx;
  position: relative;
  overflow-x: auto;
  overflow-y: hidden;
  white-space: nowrap;
}


.item {
  display: inline-block;
  text-align: center;
  width: 20%;
  flex-shrink: 0;
  font-size: 28upx;
  color: #666666;
}

.active {
  color: #000;
  position: relative;
  bottom: 4upx;
  font-weight: bold;
  font-size: 32upx;
}

.line {
  position: absolute;
  top: 32upx;
  text-align: center;
  transition: all .2s;
}

.line text {
  display: inline-block;
  width: 36upx;
  height: 8upx;
  border-radius: 32upx;
  background-color: #FF483C;
}

.content {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding-top: 90upx;
}

.box {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
</style>

在父组件中

tabs @changeTab="changeTab" :active="active" :tabs="tabTitles" :scrollLeftAct="2" :scrollRgihtAct="4">
      <empty v-if="orderList.length === 0" content="暂无订单" />
      <order-list v-else />
    </tabs>
data() {
    return {
        active: 0,
        tabTitles: [
            "全部",
            "待邮寄",
            "待验机",
            "待确认",
            "完成",
            "已取消",
            "已取消",
         ],
    }
}