1<template>2 <div3 class="w-full h-full outline-none overflow-hidden relative flex justify-center items-center cursor-grab active:cursor-grabbing"4 tabindex="-1"5 @pointerdown="onPressDown"6 >7 <div ref="containerRef" class="absolute will-change-transform cursor-default">8 <slot />9 </div>10 </div>83 collapsed lines
11</template>12
13<script setup>14/**15 * 鼠标按下时的位置16 */17const position = {18 x: 0,19 y: 020}21
22/**23 * @constant24 * @type {import('vue').Ref<HTMLElement>}25 * @description 用于获取容器的 ref26 */27const containerRef = ref()28
29function getPositions (ev) {30 if (ev instanceof MouseEvent) {31 return {32 x: ev.clientX,33 y: ev.clientY34 }35 }36
37 return {38 x: ev.touches[0].clientX,39 y: ev.touches[0].clientY40 }41}42
43/**44 * 当按下鼠标或手指45 * @param {MouseEvent|TouchEvent} ev 事件对象46 */47function onPressDown (ev) {48 // 如果不是点击的鼠标左键,则不处理49 if (ev instanceof MouseEvent && ev.button !== 0) {50 return51 }52
53 // 防止在拖动的过程中选中文字54 document.body.classList.add('select-none')55
56 // 记录按下时的位置57 Object.assign(position, getPositions(ev))58
59 document.addEventListener('pointerup', onPressUp, { once: true })60 document.addEventListener('pointermove', onPressMove, { passive: true })61}62
63/**64 * 当按下并移动鼠标或手指65 * @param {MouseEvent|TouchEvent} ev 事件对象66 */67function onPressMove (ev) {68 const { x, y } = getPositions(ev)69
70 const elStyle = containerRef.value.style71
72 // 计算鼠标/手指按下到这一次移动的偏移量73 const offsetX = x - position.x74 const offsetY = y - position.y75
76 // 获取当前的 transform 位置77 const [, currentX = 0, currentY = 0] = elStyle.transform.match(/translate3d\((.+?)px, (.+?)px, 0px\)/) ?? []78
79 elStyle.transform = `translate3d(${~~currentX + offsetX}px, ${~~currentY + offsetY}px, 0)`80
81 // 更新上一次移动的位置82 Object.assign(position, { x, y })83}84
85/**86 * 当松开鼠标或手指87 */88function onPressUp () {89 document.body.classList.remove('select-none')90 document.removeEventListener('pointermove', onPressMove)91}92
93</script>