pc端实现能横向拖拽的tab导航

56 阅读2分钟

首先说一下怎么实现的

通过比较鼠标移动是否超过7px,判定是否属于点击事件,

拖拽是通过监听mousemove事件,通过根据鼠标位置,动态改变盒子的translate值实现的

详细的请看代码

对了,因为封装的时候想搞一个方块背景色样式,一种带下边框的样式,但到这里没写完,只有带下边框的;这个组件我尝试用ts去写,但ts说实话有点忘了,不过至少没有报错,乐

效果图:

image.png

<template>
  <div class="DragScrollX">
    <div class="DragScrollX-view">
      <ul class="DragScrollX-scroll-box" ref="REF" :style="`--move: ${translateX}px`">
        <li
          :class="{ active: selectedIndex === index }"
          :style="`--activeColor: ${activeColor};
          --textColor: ${textColor};
          --itemBgc: ${itemBgc}`"
          @mousedown="hMousedown($event, index)"
          v-for="(item, index) in list"
        >
          <slot>
            <span>{{ item?.key || item }}</span>
            <span v-if="item?.value">{{ `(${item.value})` }}</span>
          </slot>
        </li>
      </ul>
    </div>
    <el-icon class="icon"><ArrowRightBold /></el-icon>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ArrowRightBold } from '@element-plus/icons-vue'

type RGB = `rgb(${number}, ${number}, ${number})`
type RGBA = `rgba(${number}, ${number}, ${number}, ${number})`
type HEX = `#${string}`

type Color = RGB | RGBA | HEX | 'transparent'
type obj = {
  key: string
  value: number | string
}
type list1 = obj[]
type list2 = string[]

const props = defineProps<{
  list: list1 & list2
  itemBgc: {
    type: Color
    default: 'transparent'
  }
  textColor: {
    type: Color
    default: '#fff'
  }
  activeColor: {
    type: Color
    default: '#409eff'
  }
}>()

const selectedIndex = ref(0)
const index1 = ref(0)
const moveOk = ref(false)
const nextX = ref(0)
const translateX = ref(0)
const startX = ref(0)
const REF = ref()

const hClick = () => {
  selectedIndex.value = index1.value
}

const hMousedown = (e: MouseEvent, index: number) => {
  index1.value = index
  startX.value = e.clientX
  nextX.value = e.clientX

  window.addEventListener('mousemove', hMousemove)
  window.addEventListener('mouseup', hMouseup)
}
const hMouseup = () => {
  window.removeEventListener('mousemove', hMousemove)
  const box = document.querySelector('.DragScrollX-scroll-box') as HTMLElement
  const view = document.querySelector('.DragScrollX-view') as HTMLElement
  const width = -box.offsetWidth + view.offsetWidth
  if (translateX.value < width) translateX.value = width
  if (translateX.value > 0) translateX.value = 0
  if (!moveOk.value && index1.value) {
    hClick()
    index1.value = 0
  }
  moveOk.value = false
  window.removeEventListener('mouseup', hMouseup)
}
const hMousemove = (e: MouseEvent) => {
  moveOk.value = Math.abs(e.clientX - startX.value) >= 7
  if (moveOk.value) {
    const distance = e.clientX - nextX.value
    nextX.value = e.clientX
    translateX.value += distance
  }
}
</script>

<style scoped lang="less">
:root {
  --activeColor: #409eff;
  --textColor: #fff;
  --itemBgc: transparent;
  --move: 0px;
}
.DragScrollX {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
  .DragScrollX-view {
    height: 100%;
    flex: 1;
    overflow: hidden;
    position: relative;
    .DragScrollX-scroll-box {
      position: absolute;
      transform: translateX(var(--move));
      white-space: nowrap;
      li {
        box-sizing: border-box;
        display: inline-block;
        padding: 0 4px 8px;
        font-size: 12px;
        cursor: pointer;
        color: var(--textColor);
        background-color: var(--itemBgc);
        &.active {
          position: relative;
          color: var(--activeColor);
          border-bottom: 2px solid var(--activeColor);
          &::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
            width: 0;
            height: 0;
            border: 4px solid transparent;
            border-bottom: 4px solid var(--activeColor);
          }
        }
      }
    }
  }
  .icon {
    color: #d5e4ff;
    margin-top: 3px;
    margin-left: 10px;
    border-left: 2px solid #d5e4ff;
  }
}
</style>