目录
自定义tabs+索引列表,支持左右滑动切换
html
复制代码
<view class="container">
<scroll-view
class="scroll-view"
scroll-x="true"
:scroll-left="scrollLeft"
scroll-with-animation
show-scrollbar="false"
>
<view
v-for="(item, index) in list"
:key="`${item.dictValue}-${index}-${item.dictCode}`"
class="scroll-item"
@click="handleItemClick(index)"
:style="{
color:
(index === currentIndex ? computedActiveColor : computedColor) +
' !important',
}"
>
<text>{{ item.dictLabel }}</text>
</view>
<!-- 指示条 -->
<view
v-if="list.length"
class="indicator"
:style="{
...indicatorStyle,
background: lineColor,
height: lineHeight,
borderRadius: lineBorderRadius,
}"
></view>
</scroll-view>
</view>
javascript
复制代码
props: {
list: {
type: Array,
default: () => [],
},
color: {
type: String,
default: null,
},
activeColor: {
type: String,
default: null,
},
lineColor: {
type: String,
default: "rgba(0,0,0,0.5)",
},
lineHeight: {
type: String,
default: "6rpx",
},
lineBorderRadius: {
type: String,
default: "3rpx",
},
currentIndex: {
type: Number,
default: 0,
},
},
data() {
return {
itemWidths: [], // 存储所有项的宽度
containerWidth: 0, // 容器宽度
scrollLeft: 0,
indicatorLeft: 0,
indicatorWidth: 60,
};
},
computed: {
...mapGetters(["appTheme"]),
computedColor() {
if (this.color) return this.color;
return this.appTheme === "dark" ? "#999999" : "#333333";
},
computedActiveColor() {
if (this.activeColor) return this.activeColor;
return this.appTheme === "dark" ? "#ffffff" : "#000000";
},
indicatorStyle() {
return {
width: this.itemWidths[this.currentIndex]
? `${this.itemWidths[this.currentIndex]}px`
: "60px",
transform: `translateX(${this.calculateIndicatorPosition()}px)`,
};
},
},
watch: {
list: {
handler() {
this.$nextTick(() => {
this.getItemWidths();
});
},
deep: true,
},
currentIndex: {
handler(newIndex) {
this.$nextTick(() => {
this.calculateScrollPosition();
});
},
},
},
mounted() {
this.getItemWidths();
},
methods: {
getItemWidths(index = 0) {
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query
.select(".scroll-view")
.boundingClientRect((containerData) => {
if (containerData) {
this.containerWidth = containerData.width;
}
})
.exec();
query
.selectAll(".scroll-item")
.boundingClientRect((data) => {
console.log("getItemWidths data", data);
if (data) {
// 保存所有项的宽度
this.itemWidths = data.map((item) => item.width);
// 计算当前项的位置
this.calculateScrollPosition();
}
})
.exec();
});
},
calculateScrollPosition() {
// 计算滚动位置,确保选中项在可视区域内
let totalWidth = 0;
for (let i = 0; i < this.currentIndex; i++) {
totalWidth += this.itemWidths[i] || 120; // 如果没有宽度数据,使用默认值
}
// 计算选中项的中心位置
const currentItemWidth = this.itemWidths[this.currentIndex] || 120;
const targetPosition = totalWidth + currentItemWidth / 2;
// 计算需要滚动的距离,使选中项居中
this.scrollLeft = Math.max(0, targetPosition - this.containerWidth / 2);
},
calculateIndicatorPosition() {
// 计算指示器位置
let totalWidth = 0;
for (let i = 0; i < this.currentIndex; i++) {
totalWidth += this.itemWidths[i] || 120;
}
return totalWidth;
},
handleItemClick(index) {
this.$emit("update:currentIndex", index);
this.$emit("onHandleItemClick", index);
this.$nextTick(() => {
this.calculateScrollPosition();
});
},
},
css
复制代码
.container {
width: 100%;
padding: 10rpx 0;
}
.scroll-view {
width: 100%;
white-space: nowrap;
height: 80rpx;
box-sizing: border-box;
}
.scroll-item {
display: inline-block;
padding: 0 30rpx;
height: 70rpx;
line-height: 70rpx;
text-align: center;
font-size: 30rpx;
color: #333;
}
.indicator {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
创建touch-list组件
html
复制代码
<swiper
class="swiper-container"
:current="currentIndex"
@change="handleSwiperChange"
:duration="300"
:style="{ height: swiperHeight }"
>
<swiper-item
v-for="(item, index) in listLength"
:key="index"
class="swiper-item"
>
<view class="content" v-if="index === currentIndex">
<slot></slot>
</view>
</swiper-item>
</swiper>
javascript
复制代码
props: {
currentIndex: {
type: Number,
default: 0,
},
listLength: {
type: Number,
default: 0,
},
swiperHeight: {
type: String,
default: "500rpx",
},
},
methods: {
handleSwiperChange(e) {
this.$emit("update:currentIndex", e.detail.current);
this.$emit("onHandleSwiperChange", e.detail.current);
},
},
css
复制代码
.swiper-container {
width: 100%;
}
.swiper-item {
width: 100%;
height: 100%;
}
.content {
width: 100%;
height: 100%;
overflow: scroll;
}
父组件使用
html
复制代码
<scroll-x-tabs
:list="exploreTypeDictList"
:currentIndex.sync="current"
:line-color="lineColor"
></scroll-x-tabs>
<touch-list
:list-length="exploreTypeDictList.length"
:currentIndex.sync="current"
:swiperHeight="swiperHeight"
@onHandleSwiperChange="onHandleSwiperChange"
>
<view class="learn-grid" v-if="studyList.length">
<!--列表内容-->
</view>
</touch-list>