Vue 轻量级滑动tab栏_选中的item自动居中组件

可滑动,选定的tabItem自动居中,支持x和y两种模式

<template>
  <div class="scrollBarWrapper" :style="scrollBarWrapperStyle">
    <div
      class="scrollBarContent"
      :class="direction === 'y' ? 'directionY' : 'directionX'"
      ref="scrollBarContent"
    >
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    direction: {
      type: String,
      default: "x",
      validator(value) {
        return value === "x" || value === "y";
      }
    },
    activeIndex: {
      type: Number,
      default: 0,
      validator(value) {
        return value >= 0;
      }
    }
  },
  watch: {
    activeIndex(newVal, oldVal) {
      this.handleChange();
    }
  },
  computed: {
    scrollBarWrapperStyle() {
      return this.direction === "y"
        ? {
            height: "100%"
          }
        : {
            width: "100%"
          };
    }
  },
  mounted() {
    this.initItemDisplay();
    this.handleChange();
  },
  methods: {
    initItemDisplay() {
      const content = this.$refs.scrollBarContent;
      const contentItem = content.children;
      [].forEach.call(contentItem, item => {
        if (this.direction === "y") {
          item.style.display = "block";
        } else {
          item.style.display = "inline-block";
        }
      });
    },
    handleChange() {
      this.$nextTick(() => {
        const content = this.$refs.scrollBarContent; // 发生滑动的元素
        const activeItem = content.children[this.activeIndex]; // 当前选中的元素
        if(!activeItem) return false;
        
        const scrollOption = {
          top: 0,
          left: 0,
          behavior: "smooth"
        };

        if (this.direction === "y") {
          const contentHeight = content.offsetHeight;
          const activeItemHeight = activeItem.offsetHeight;
          const activeItemTop = activeItem.offsetTop;
          const offset = activeItemTop - (contentHeight - activeItemHeight) / 2; // 需要移动的位置
          scrollOption.top = offset;
        } else {
          const contentWidth = content.offsetWidth; // 发生滑动元素的宽
          const activeItemWidth = activeItem.offsetWidth; // 当前元素的宽
          const activeItemLeft = activeItem.offsetLeft; // 当前元素的到他父盒子左侧的距离
          const offset = activeItemLeft - (contentWidth - activeItemWidth) / 2; // 需要移动的位置
          scrollOption.left = offset;
        }

        content.scrollTo(scrollOption);
      });
    }
  }
};
</script>

<style lang="scss" scoped>
.scrollBarWrapper {
  position: relative;
  overflow: hidden;
  user-select: none;
  vertical-align: middle;

  .scrollBarContent {
    width: 100%;
    white-space: nowrap;
    word-break: keep-all;
    -webkit-overflow-scrolling: touch;

    &.directionX {
      overflow-x: scroll;
      overflow-y: hidden;
    }

    &.directionY {
      overflow-x: hidden;
      overflow-y: scroll;
      height: 100%;
    }

    &::-webkit-scrollbar {
      display: none;
    }
  }
}
</style>

demo
<template>
	<scrollBar direction="x" :activeIndex="activeIndex">
		<div
			class="scrollBarItem"
			v-for="(item, index) in options"
			:key="index"
			@click="changeNav(item, index)"
			:class="index === activeIndex ? 'active' : null"
		>
			<div>{{item.name}}</div>
		</div>
	</scrollBar>
</template>
<script>
	// 先导入再注册下
	export default {
		data() {
			return {
				activeIndex: 0,
				options: [
					{id: 1, name: '关注'},
					{id: 2, name: '推荐'},
					{id: 3, name: '本地'},
					{id: 4, name: '新闻'},
					{id: 5, name: '汽车'},
					{id: 6, name: '直播'},
					{id: 7, name: '游戏'},
					{id: 8, name: '小说'},
					{id: 9, name: '美女'}
				]
			}
		},
		methods: {
			changeNav(item, index) {
				this.activeIndex = index;
			}
		}
	}
</script>
<style lang="scss" scoped>
	.scrollBarItem {
		padding: rem(8) rem(15);
	}
	.active {
		color: red;
		div {
			border-bottom: 1px solid red;
		}
	}
</style>
posted @ 2020-07-15 10:24  demo_you  阅读(1225)  评论(1编辑  收藏  举报