前端Vue技术vue3 - 如何使用指令轻松实现鼠标水平滚动
Mintal引言
在开发一体机项目时,我遇到了一个棘手的问题:
由于一体机的特殊性,其界面样式基于移动端设计,但浏览器内核仍属于 PC 端。因此,内部元素既要支持移动端的 touch 事件,又要兼容 PC 端的 mouse 事件。
实现思路
为了让鼠标滚轮能够控制水平滚动,我们可以监听 wheel
事件,并根据滚动方向调整 scrollLeft
值。
1 2 3 4 5 6
| 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()
阻止默认的滚动行为。
这样,当用户滚动鼠标滚轮时,页面会根据滚动方向和幅度进行水平滚动。
拖拽滚动
除了滚轮滚动,我们还需要支持鼠标拖拽滚动,这需要监听以下事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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
,以便下一次移动时继续计算滚动距离。
这些函数结合起来,可以实现一个拖拽滚动的效果,当用户按住鼠标并拖动时,元素会根据鼠标的移动进行滚动。
实现源码
dragUtil.ts1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| 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) }, }
|
注册指令
main.ts1 2
| import dragUtil from './util/dragUtil.ts' app.directive('drag', dragUtil)
|
使用指令