|
|
@@ -0,0 +1,113 @@
|
|
|
+<template>
|
|
|
+ <view class="fs-scroll-list">
|
|
|
+ <scroll-view
|
|
|
+ scroll-x
|
|
|
+ :show-scrollbar="false"
|
|
|
+ :lower-threshold="0"
|
|
|
+ :upper-threshold="0"
|
|
|
+ @scrolltoupper="handleToUpper"
|
|
|
+ @scrolltolower="handleToLower"
|
|
|
+ @scroll="handleScroll"
|
|
|
+ >
|
|
|
+ <view class="fs-scroll-list-content">
|
|
|
+ <slot></slot>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <view class="fs-scroll-indicator">
|
|
|
+ <view class="fs-scroll-indicator-line">
|
|
|
+ <view class="fs-scroll-indicator-bar" :style="barStyle"></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: 'fs-scroll-list'
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { onMounted, reactive, ref, computed, getCurrentInstance } from 'vue'
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ indicatorWidth: {
|
|
|
+ type: String,
|
|
|
+ default: '50px'
|
|
|
+ },
|
|
|
+ indicatorLineWidth: {
|
|
|
+ type: String,
|
|
|
+ default: '15px'
|
|
|
+ },
|
|
|
+ indicatorColor: {
|
|
|
+ type: String,
|
|
|
+ default: '#f2f2f2'
|
|
|
+ },
|
|
|
+ indicatorActiveColor: {
|
|
|
+ type: String,
|
|
|
+ default: '#3c9cff'
|
|
|
+ }
|
|
|
+})
|
|
|
+const emits = defineEmits(['left', 'right'])
|
|
|
+
|
|
|
+const state = reactive({
|
|
|
+ listWidth: 0,
|
|
|
+ contentWidth: 0,
|
|
|
+ scrollWidth: 0
|
|
|
+})
|
|
|
+onMounted(() => {
|
|
|
+ uni.createSelectorQuery().in(getCurrentInstance().ctx).select('.fs-scroll-list').boundingClientRect(data => {
|
|
|
+ state.listWidth = data.width
|
|
|
+ }).exec()
|
|
|
+})
|
|
|
+
|
|
|
+
|
|
|
+const indicatorLeftWidth = computed(() => {
|
|
|
+ return parseInt(props.indicatorWidth) - parseInt(props.indicatorLineWidth)
|
|
|
+})
|
|
|
+const listLeftWidth = computed(() => {
|
|
|
+ return state.contentWidth - state.listWidth
|
|
|
+})
|
|
|
+const barStyle = computed(() => {
|
|
|
+ const x = state.scrollWidth * indicatorLeftWidth.value / listLeftWidth.value + 'px'
|
|
|
+ return `transform: translateX(${x})`
|
|
|
+})
|
|
|
+
|
|
|
+const handleScroll = event => {
|
|
|
+ state.scrollWidth = event.detail.scrollLeft
|
|
|
+ state.contentWidth = event.detail.scrollWidth
|
|
|
+}
|
|
|
+
|
|
|
+const handleToUpper = () => emits('left')
|
|
|
+const handleToLower = () => emits('right')
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.fs-scroll-list{
|
|
|
+ &-box{
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+ &-content{
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ }
|
|
|
+}
|
|
|
+.fs-scroll-indicator{
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ &-line{
|
|
|
+ background-color: v-bind(indicatorColor);
|
|
|
+ width: v-bind(indicatorWidth);
|
|
|
+ height: 8rpx;
|
|
|
+ border-radius: 50rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+ &-bar{
|
|
|
+ background-color: v-bind(indicatorActiveColor);
|
|
|
+ height: 8rpx;
|
|
|
+ width: v-bind(indicatorLineWidth);
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|