vue組件系列-列表左右箭頭滾動(vue3+ts)

<template>
  <div class="list-wrapper" ref="allListRef">
     <div v-if="showArrow && listSource.length > minArrowItemsCount" 
        @click="scrollLeft" 
        class="arrow"
        :class="{'disable': leftArrowDisabled }"
        >&lt;</div>
     <div class="list" ref="listRef" :class="{'overflowX': showArrow}">
       <div class="items-box" ref="itemsRef">
         <slot></slot>
       </div>
     </div>
     <div v-if="showArrow && listSource.length > minArrowItemsCount" 
      @click="scrollRight" 
      class="arrow"
      :class="{'disable': rightArrowDisabled }"
      >&gt;</div>
  </div>
</template>

<script setup lang="ts">
import { nextTick, reactive, ref } from 'vue'

const props = withDefaults(
   defineProps<{
      listSource: Array<any>,
      showArrow: boolean,
      minArrowItemsCount: number, //最小項目數 超過才顯示箭頭
      itemWidth: number, //列表每一項的寬度(包含每項間距)
      moveItemsCount: number //每次點擊箭頭移動的item數量
   }>(),
   {
      showArrow: true,
      minArrowItemsCount: 5,
      itemWidth: 96,
      moveItemsCount: 1
   }
)

const listRef = ref(null)
const itemsRef = ref(null)

const leftArrowDisabled = ref(true)
const rightArrowDisabled = ref(false)

// 左滑動邏輯
const scrollLeft = () => {
   nextTick(() => {
      if (listRef.value && itemsRef.value) {
         const allLength = props.listSource.length * props.itemWidth
         const boxLength = listRef.value.clientWidth
         const moveDistance = props.moveItemsCount * props.itemWidth
         if (allLength < boxLength) return
         
         rightArrowDisabled.value = false
         const listEl = itemsRef.value
         const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
         if (leftMove + boxLength - moveDistance < boxLength) {
            // 到最開始的時候
            listEl.style.left = '0px'
            leftArrowDisabled.value = true
         } else {
            listEl.style.left = '-' + (leftMove - moveDistance) + 'px'
            leftArrowDisabled.value = false
         }
      } 
   })
}
// 右滑動邏輯
const scrollRight = () => {
   nextTick(() => {
      if (listRef.value && itemsRef.value) { 
         const allLength = props.listSource.length * props.itemWidth
         const boxLength = listRef.value.clientWidth
         const moveDistance = props.moveItemsCount * props.itemWidth
         if (allLength < boxLength) return
         
         leftArrowDisabled.value = false
         const listEl = itemsRef.value
         const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
         if (leftMove + boxLength + moveDistance > allLength) {
            listEl.style.left = '-' + (allLength - boxLength) + 'px'
            rightArrowDisabled.value = true
         } else {
            listEl.style.left = '-' + (leftMove + moveDistance) + 'px'
            rightArrowDisabled.value = false
         }
      }
   })
}

</script>

<style scoped lang="less">
.list-wrapper {
   width: 100%;
   display: flex;
   justify-content: space-between;

   .list {
      margin-left:8px;
      margin-right:8px;
      width: calc(100% - 48px);
   }
   .overflowX {
     overflow-x: hidden;
   }

   .items-box {
      display: flex;
      transform: all 1s;
      position: relative;
      left: 0;
      transition: left .5s;
      width: 100%;
   }

   .arrow {
      width: 16px;
      height: 60px;
      background: #FFFFFF;
      border-radius: 2px;
      border: 1px solid #E5E5E5;
      line-height: 60px;
      text-align: center;
      font-size: 16px;
      color: #333333;
      cursor:pointer;
      user-select: none;
      -webkit-user-select: none;
   }

   .arrow:not(.disable):hover,
   .arrow:not(.disable):active {
     background: #FAFAFA;
   }

   .disable {
     color:#E5E5E5;
     cursor:default;
   }
}
</style>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章