<template> <view class="tabBlock" v-if="list.length > 0"> <scroll-view scroll-x="true" scroll-with-animation :scroll-left="tabsScrollLeft" @scroll="scroll"> <view class="tab" id="tab_list"> <!-- 循环体 --> <view v-for="(item, index) in list" :key="index" :class="['tab__item', {'tab__item--active': currentIndex === index}]" :style="{color: (currentIndex === index ? `${itemColor}`: '')}" id="tab_item" @click="select(item, index)"> <!-- 标题 --> <!-- <view><slot name="title" :title="item.title"></slot></view> --> <!-- 标题 --> <view v-if="!showTitleSlot">{{item.title}}</view> </view> </view> <!-- 下划线 --> <view class="tab__line" :style="{background: lineColor, width: lineStyle.width, transform: lineStyle.transform,transitionDuration: lineStyle.transitionDuration}"> </view> </scroll-view> </view> </template> <script> export default { props: { value: [Number, String], list: { // 传值 type: Array, default: () => { return [ {title:'标题一'}, {title:'标题二'}, {title:'标题三'}, {title:'标题四'}, {title:'标题五'}, {title:'标题六'}, {title:'标题七'}, {title:'标题八'}, {title:'标题九'}, ] } }, itemColor: String, // tab内容颜色 lineColor: String, // 下划线颜色 lineAnimated: { // 是否展示下划线动画 type: Boolean, default: true } }, data() { return { currentIndex: 0, lineStyle: {}, scrollLeft: 0, tabsScrollLeft: 0, duration: 0.3 } }, computed: { showTitleSlot() { return this.$scopedSlots.title } }, watch: { list() { this.setTabList() }, value() { this.currentIndex = this.value this.setTabList() } }, mounted() { this.currentIndex = this.value this.setTabList() if (!this.lineAnimated) { this.duration = 0 } console.log(this.$scopedSlots) }, methods: { select(item, index) { this.$emit('input', index) }, setTabList() { this.$nextTick(() => { if (this.list.length > 0) { this.setLine() this.scrollIntoView() } }) }, setLine() { let lineWidth = 0, lineLeft = 0 this.getElementData(`#tab_item`, (data) => { let el = data[this.currentIndex] this.$emit('tabheight',el.height) lineWidth = el.width / 2 // lineLeft = el.width * (this.currentIndex + 0.5) // 此种只能针对每个item长度一致的 lineLeft = el.width / 2 + (-data[0].left) + el.left this.lineStyle = { width: `${lineWidth}px`, transform: `translateX(${lineLeft}px) translateX(-50%)`, transitionDuration: `${this.duration}s` }; }) }, scrollIntoView() { // item滚动 let lineLeft = 0; this.getElementData('#tab_list', (data) => { let list = data[0] this.getElementData(`#tab_item`, (data) => { let el = data[this.currentIndex] // lineLeft = el.width * (this.currentIndex + 0.5) - list.width / 2 - this.scrollLeft lineLeft = el.width / 2 + (-list.left) + el.left - list.width / 2 - this.scrollLeft this.tabsScrollLeft = this.scrollLeft + lineLeft }) }) }, getElementData(el, callback) { uni.createSelectorQuery().in(this).selectAll(el).boundingClientRect().exec((data) => { callback(data[0]); }); }, scroll(e) { this.scrollLeft = e.detail.scrollLeft; } } } </script> <style lang="scss"> .tabBlock { position: relative; background: #fff; .tab { position: relative; display: flex; font-size: 28rpx; padding-bottom: 15rpx; white-space: nowrap; &__item { // flex: 1; width: 20%; flex-shrink: 0; text-align: center; line-height: 90rpx; color: #868695; &--active { color: $uni-color-primary; font-weight: bold; } &-title { margin: 0 40rpx; } } } .tab__line { display: block; height: 6rpx; position: absolute; bottom: 20rpx; left: 0; z-index: 1; border-radius: 6rpx; position: relative; background: $uni-color-primary; } } </style>