小程序轮子-自定义 tab 选项卡

1.支持在父组件自定义样式
2.支持父组件中指定数据
3.支持在父组件中选中项

VUE文件:

<template>
	<!--
		my-tab组件
		1.支持在父组件自定义样式
		2.支持父组件中指定数据
		3.支持在父组件中选中项
	 -->
  <view class="tab-container">
    <view class="tab-box">
      <scroll-view
        id="_scroll"
        scroll-x
        class="scroll-view"
        scroll-with-animation
        :scroll-left="scrollLeft"
      >
        <view class="scroll-content">
          <view class="tab-item-box">
            <block v-for="(item, index) in tabList" :key="index">
              <view
                class="tab-item"
                :id="'_tab_' + index"
                :class="{ 'tab-item-active': activeIndex === index }"
                @click="tabClick(index)"
                :style="{
                  color:
                    activeIndex === index ? defaultConfig.activeTextColor : defaultConfig.textColor
                }"
                >{{ item.label || item }}</view
              >
            </block>
          </view>
          <!-- 滑块 -->
          <view
            class="underLine"
            :style="{
              transform: 'translateX(' + slider.left + 'px)',
              width: defaultConfig.underLineWidth + 'px',
              height: defaultConfig.underLineHeight + 'px',
              backgroundColor: defaultConfig.underLineColor
            }"
          />
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'my-tabs',
  props: {
    // 父组件传入的 tabs 数据
    tabData: {
      type: Array,
      default: () => []
    },
    // 默认激活项
    defaultIndex: {
      type: Number,
      default: 0
    },
    // 配置对象
    config: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  data: () => {
    return {
      // 内部维护的数据对象,为每个 item 单独额外维护一个 slider 的滑块对象
      tabList: [],
      // 当前激活项的 index
      activeIndex: -1,
      // 滑块对象
      slider: {
        // 距离左侧的距离
        left: 0
      },
      // scrollView 的横向滚动条位置
      scrollLeft: 0,
      // 默认配置
      defaultConfig: {
        // 默认的字体颜色
        textColor: '#333333',
        // 高亮字体颜色
        activeTextColor: '#f94d2a',
        // 下划线宽度 px
        underLineWidth: 24,
        // 下划线高度 px
        underLineHeight: 2,
        // 下划线颜色
        underLineColor: '#f94d2a'
      }
    };
  },
  // 侦听器
  watch: {
    // 侦听数据的变化
    tabData: {
      handler(val) {
        this.tabList = val;
        setTimeout(() => {
          this.updateTabWidth();
        }, 0);
      },
      // 该回调将会在侦听开始之后被立即调用
      immediate: true
    },
    // 监听激活项目的变化
    defaultIndex: {
      handler(val) {
        this.activeIndex = val;
        // 定义滑块的位置
        this.tabToIndex();
      },
      // 该回调将会在侦听开始之后被立即调用
      immediate: true
    },
    // 监听 config
    config: {
      handler(val) {
        this.defaultConfig = { ...this.defaultConfig, ...val };
      },
      // 该回调将会在侦听开始之后被立即调用
      immediate: true
    }
  },
  methods: {
    /**
     * 更新 tab item 的宽度
     */
    updateTabWidth() {
      /**
       * 为 tabList 的每个 item 单独额外维护一个 slider 的滑块对象
       */
      let data = this.tabList;
      if (data.length == 0) return false;

      // 获取 dom 的固定写法
      const query = uni.createSelectorQuery().in(this);
      // 循环数据源
      data.forEach((item, index) => {
        // 获取 dom 的固定写法
        query
          .select('#_tab_' + index)
          .boundingClientRect((res) => {
            // 为数据对象中每一个 item 都维护一个 _slider(滑动条) 对象
            item._slider = {
              // 当前的 tab 距离左侧的距离
              left: res.left + (res.width - this.defaultConfig.underLineWidth) / 2
            };
            // 运算完成之后,执行一次 【滑块】位置运算
            if (data.length - 1 === index) {
              this.tabToIndex();
            }
          })
          .exec();
      });
    },
    /**
     * tab 的点击事件处理
     */
    tabClick(index) {
      this.activeIndex = index;
      // 定义滑块的位置
      this.tabToIndex();
      // 发送通知
      this.$emit('tabClick', index);
    },
    /**
     * 根据当前的 activeIndex 下标,计算 【滑块】 滚动位置
     */
    tabToIndex() {
      if (this.tabList.length === 0) return;
      // 获取当前的 activeIndex
      const activeIndex = this.activeIndex;
      // 滑块的宽度
      const underLineWidth = this.defaultConfig.underLineWidth;
      // 配置 滚动条 的数据
      this.slider = {
        // TODO:left 如何定义呢?
        // 1. 维护一个单独的数据对象 `tabList`
        // 2. 在 `tabList`  的 `item` 中为一个 `_slider` 属性
        // 3. 该属性保存了 【当前 `item` 下 的滑块位置】
        //    3.1. 计算公式:`滑块左侧位置 = item.left + (item.width - slider.width) / 2`
        left: this.tabList[activeIndex]._slider.left
      };
      // 为 scrollView 设置滚动位置
      this.scrollLeft = this.activeIndex * this.defaultConfig.underLineWidth;
    }
  }
};
</script>

<style lang="scss" scoped>
.tab-container {
  font-size: $uni-font-size-base;
  height: 45px;
  line-height: 45px;
  background-color: $uni-bg-color;
  .tab-box {
    width: 100%;
    height: 45px;
    display: flex;
    position: relative;
    .scroll-view {
      white-space: nowrap;
      width: 100%;
      height: 100%;
      box-sizing: border-box;
      .scroll-content {
        width: 100%;
        height: 100%;
        position: relative;

        .tab-item-box {
          height: 100%;
          .tab-item {
            height: 100%;
            display: inline-block;
            text-align: center;
            padding: 0 15px;
            position: relative;
            text-align: center;
            color: $uni-text-color;

            &-active {
              color: red;
            }
          }
        }
        .underLine {
          height: 2px;
          width: 25px;
          background-color: #f01414;
          border-radius: 3px;
          transition: 0.5s;
          position: absolute;
          bottom: 0;
        }
      }
    }
  }

  /* #ifdef H5 */
  /deep/.uni-scroll-view::-webkit-scrollbar {
    display: none;
  }

  /deep/.uni-scroll-view {
    scrollbar-width: none;
  }
  /* #endif */
}
</style>

 

 

 

|| 版权声明
作者:云言
链接:https://yyink.cn/archives/359.html
声明:如无特别声明本文即为原创文章仅代表个人观点,版权归《云言博客》所有,欢迎转载,转载请保留原文链接。
THE END
分享
二维码
海报
小程序轮子-自定义 tab 选项卡
1.支持在父组件自定义样式 2.支持父组件中指定数据 3.支持在父组件中选中项 VUE文件: <template> <!-- my-tab组件 1.支持在父组件自定义……
<<上一篇
下一篇>>