首先说一下怎么实现的
通过比较鼠标移动是否超过7px,判定是否属于点击事件,
拖拽是通过监听mousemove事件,通过根据鼠标位置,动态改变盒子的translate值实现的
详细的请看代码
对了,因为封装的时候想搞一个方块背景色样式,一种带下边框的样式,但到这里没写完,只有带下边框的;这个组件我尝试用ts去写,但ts说实话有点忘了,不过至少没有报错,乐
效果图:
<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>