前言
这是vue3指令插件的第二篇,这次实现的是一个拖拽滚动的插件。主要的效果是可以通过拖拽和滚轮来滚动横向列表。类似移动端的拖拽滚动。
这个插件可以用在那种横向列表,又需要滚动的需求上。
-
目前实现效果:
-
滚轮滚动
-
拖拽滚动
-
效果展示
实现思路
实现思路这边的部分描述,是我直接用ai生成的。小偷一点懒
滚轮滚动
const onWheel = (e: any) => {
const delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail))
const target = e.currentTarget as DraggableElement | null
if (target) target.scrollLeft -= delta * 450
e.preventDefault()
}
这段代码的作用是处理鼠标滚轮事件,主要是为了实现拖拽滚动的效果。具体来说:
onWheel函数接收一个事件对象e。- 计算滚轮滚动的方向和幅度,结果保存在
delta变量中。 - 获取触发事件的目标元素
target,并将其类型断言为DraggableElement。 - 如果
target存在,调整其scrollLeft属性,使页面滚动。 - 调用
e.preventDefault()阻止默认的滚动行为。 这样,当用户滚动鼠标滚轮时,页面会根据滚动方向和幅度进行水平滚动。
拖拽滚动
拖拽滚动需要监听几个事件:
- 鼠标按下事件
- 鼠标移出事件
- 鼠标移动事件
// 鼠标移出事件
const onMouseUpOrLeave = (el: DraggableElement) => () => {
el.isMouseDown = false
}
// 鼠标移动事件
const onMouseMove = (el: DraggableElement) => (e: MouseEvent) => {
if (el.isMouseDown) {
const moveX = e.clientX
const target = e.currentTarget as DraggableElement
const scrollX = moveX - el.startX
if (target) {
target.scrollLeft = el.scrollLeft - scrollX
el.scrollLeft = target.scrollLeft
el.startX = e.clientX
}
}
}
// 鼠标按下事件
const onMouseDown = (el: DraggableElement) => (e: any) => {
el.isMouseDown = true
el.startX = e.clientX
el.scrollLeft = e.currentTarget.scrollLeft
}
这三个函数用于实现一个简单的拖拽滚动效果,具体功能如下:
onMouseDown函数:
-
这是一个鼠标按下事件处理函数。
-
当鼠标按下时,设置
el.isMouseDown为true,表示鼠标已经按下。 -
记录鼠标按下时的 X 坐标
e.clientX。 -
记录当前元素的
scrollLeft值到el.scrollLeft,用于后续计算滚动距离。
onMouseUpOrLeave 函数:
- 这是一个鼠标松开或移出事件处理函数。
- 当鼠标松开或移出时,设置
el.isMouseDown为false,表示鼠标已经松开或移出了元素。
onMouseMove函数:
- 这是一个鼠标移动事件处理函数。
- 当鼠标移动时,如果
el.isMouseDown为true,表示鼠标处于按下状态,执行滚动操作。 - 计算鼠标移动的距离
moveX - el.startX。 - 根据鼠标移动的距离调整元素的
scrollLeft值,实现滚动效果。 - 更新
el.scrollLeft,以便下一次移动时继续计算滚动距离。
这些函数结合起来,可以实现一个拖拽滚动的效果,当用户按住鼠标并拖动时,元素会根据鼠标的移动进行滚动。
实现源码
interface DraggableElement extends HTMLElement {
isMouseDown?: boolean
scrollLeft: number
startX: number
}
// 鼠标按下事件
const onMouseDown = (el: DraggableElement) => (e: any) => {
el.isMouseDown = true
el.startX = e.clientX
el.scrollLeft = e.currentTarget.scrollLeft
}
// 滚轮事件
const onWheel = (e: any) => {
const delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail))
const target = e.currentTarget as DraggableElement | null
if (target) target.scrollLeft -= delta * 450
e.preventDefault()
}
// 鼠标移出事件
const onMouseUpOrLeave = (el: DraggableElement) => () => {
el.isMouseDown = false
}
// 鼠标移动事件
const onMouseMove = (el: DraggableElement) => (e: MouseEvent) => {
if (el.isMouseDown) {
const moveX = e.clientX
const target = e.currentTarget as DraggableElement
const scrollX = moveX - el.startX
if (target) {
target.scrollLeft = el.scrollLeft - scrollX
el.scrollLeft = target.scrollLeft
el.startX = e.clientX
}
}
}
export default {
created(el: DraggableElement) {
el.startX = 0
el.isMouseDown = false
el.scrollLeft = 0
el.style.setProperty('user-select', 'none')
},
mounted(el: DraggableElement) {
el.addEventListener('mousedown', onMouseDown(el) as EventListener)
el.addEventListener('wheel', onWheel as EventListener)
el.addEventListener('mouseup', onMouseUpOrLeave(el) as EventListener)
el.addEventListener('mouseleave', onMouseUpOrLeave(el) as EventListener)
el.addEventListener('mousemove', onMouseMove(el) as EventListener)
},
unmounted(el: DraggableElement) {
el.removeEventListener('mousedown', onMouseDown(el) as EventListener)
el.removeEventListener('wheel', onWheel as EventListener)
el.removeEventListener('mouseup', onMouseUpOrLeave(el) as EventListener)
el.removeEventListener('mouseleave', onMouseUpOrLeave(el) as EventListener)
el.removeEventListener('mousemove', onMouseMove(el) as EventListener)
},
}
使用方法
注册指令
-
这是一个vue3的指令,在
main.ts里面注册这个指令 -
import dragAndDrop from './modules/dragAndDrop' app.directive('dragAndDrop',dragAndDrop)为了方便我这里,将所有的指令的注册都放到
index.ts文件里面 然后统一注册
**index.ts文件代码**
```ts
import type { App } from 'vue'
import tableAutoScroll from './modules/tableAutoScroll'
import dragAndDrop from './modules/dragAndDrop'
const directives: Record<string, any> = {
tableAutoScroll,
dragAndDrop,
}
/**
* @function 批量注册指令
* @param app vue 实例对象
*/
export const install = (app: App): void => {
Object.keys(directives).forEach((key) => {
app.directive(key, directives[key]) // 将每个directive注册到app中
})
}
```
在组件中使用
```vue
<template>
<article class="container">
<p>横向滚动</p>
<section v-dragAndDrop class="transverse">
<el-row>
<el-col v-for="item in 20" :key="item" :span="6">
<el-card>
<p>卡片 {{ item }}</p>
</el-card>
</el-col>
</el-row>
</section>
</article>
</template>
<script setup lang="ts"></script>
<style lang="scss" scoped>
.container {
height: 100%;
display: flex;
flex-direction: column;
padding: 24px 0;
align-items: center;
box-sizing: border-box;
}
.transverse {
position: relative;
width: 80%;
height: 100px;
overflow: hidden;
overflow-x: auto;
.el-row {
flex-wrap: nowrap;
}
}
</style>
```
结尾
自从写了第一篇列表滚动的插件,我就想要多实现几个这种通过vue指令去使用的插件,并把它做成一个开源的工具库。掘友们,有什么意见或者思路欢迎在评论出指出。下一篇会做一个拖拽的指令。
github仓库:github.com/leixq1024/v…
目前仓库什么都没有,只是单纯的把代码上传了,后续会补全readme文件,同时搭建项目示例