2025-02-13 03:53:24 +00:00
|
|
|
<template>
|
|
|
|
<div
|
|
|
|
class="draggable-container"
|
|
|
|
:style="containerStyle"
|
|
|
|
@mousedown="startDrag"
|
|
|
|
@touchstart.prevent="startDrag"
|
|
|
|
>
|
|
|
|
<slot />
|
|
|
|
</div>
|
|
|
|
</template>
|
2025-02-12 08:50:52 +00:00
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
<script setup>
|
2025-02-12 08:50:52 +00:00
|
|
|
const props = defineProps({
|
2025-02-13 03:53:24 +00:00
|
|
|
fixedSide: {
|
|
|
|
type: String,
|
|
|
|
default: 'none',
|
|
|
|
validator: (v) => ['left', 'right', 'top', 'bottom', 'none'].includes(v)
|
|
|
|
},
|
2025-02-12 08:50:52 +00:00
|
|
|
initialPosition: {
|
|
|
|
type: Object,
|
|
|
|
default: () => ({ x: 0, y: 0 })
|
|
|
|
},
|
2025-02-13 03:53:24 +00:00
|
|
|
width: Number,
|
|
|
|
height: Number
|
2025-02-12 08:50:52 +00:00
|
|
|
})
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
const position = ref({ ...props.initialPosition })
|
2025-02-12 08:50:52 +00:00
|
|
|
const isDragging = ref(false)
|
2025-02-13 03:53:24 +00:00
|
|
|
const startPosition = ref({ x: 0, y: 0 })
|
2025-02-12 08:50:52 +00:00
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
const containerStyle = computed(() => ({
|
2025-02-12 08:50:52 +00:00
|
|
|
position: 'fixed',
|
2025-02-13 03:53:24 +00:00
|
|
|
width: props.width ? `${props.width}px` : 'auto',
|
|
|
|
height: props.height ? `${props.height}px` : 'auto',
|
|
|
|
left: `${position.value.x}px`,
|
|
|
|
top: `${position.value.y}px`,
|
|
|
|
cursor: isDragging.value ? 'grabbing' : 'grab'
|
2025-02-12 08:50:52 +00:00
|
|
|
}))
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
const getClientPos = (e) => ({
|
|
|
|
x: e.touches ? e.touches[0].clientX : e.clientX,
|
|
|
|
y: e.touches ? e.touches[0].clientY : e.clientY
|
|
|
|
})
|
|
|
|
|
|
|
|
const startDrag = (e) => {
|
|
|
|
isDragging.value = true
|
|
|
|
const { x, y } = getClientPos(e)
|
|
|
|
startPosition.value = {
|
|
|
|
x: x - position.value.x,
|
|
|
|
y: y - position.value.y
|
2025-02-12 08:50:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
const onDrag = (e) => {
|
|
|
|
if (!isDragging.value) return
|
2025-02-12 08:50:52 +00:00
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
const { x: clientX, y: clientY } = getClientPos(e)
|
|
|
|
const maxX = window.innerWidth - (props.width || 0)
|
|
|
|
const maxY = window.innerHeight - (props.height || 0)
|
2025-02-12 08:50:52 +00:00
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
let newX = clientX - startPosition.value.x
|
|
|
|
let newY = clientY - startPosition.value.y
|
2025-02-12 08:50:52 +00:00
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
// 根据固定边限制移动方向
|
|
|
|
switch (props.fixedSide) {
|
2025-02-12 08:50:52 +00:00
|
|
|
case 'left':
|
2025-02-13 03:53:24 +00:00
|
|
|
newX = 0
|
|
|
|
newY = Math.max(0, Math.min(newY, maxY))
|
2025-02-12 08:50:52 +00:00
|
|
|
break
|
|
|
|
case 'right':
|
2025-02-13 03:53:24 +00:00
|
|
|
newX = maxX
|
|
|
|
newY = Math.max(0, Math.min(newY, maxY))
|
2025-02-12 08:50:52 +00:00
|
|
|
break
|
|
|
|
case 'top':
|
2025-02-13 03:53:24 +00:00
|
|
|
newX = Math.max(0, Math.min(newX, maxX))
|
|
|
|
newY = 0
|
2025-02-12 08:50:52 +00:00
|
|
|
break
|
|
|
|
case 'bottom':
|
2025-02-13 03:53:24 +00:00
|
|
|
newX = Math.max(0, Math.min(newX, maxX))
|
|
|
|
newY = maxY
|
2025-02-12 08:50:52 +00:00
|
|
|
break
|
2025-02-13 03:53:24 +00:00
|
|
|
default:
|
|
|
|
newX = Math.max(0, Math.min(newX, maxX))
|
|
|
|
newY = Math.max(0, Math.min(newY, maxY))
|
2025-02-12 08:50:52 +00:00
|
|
|
}
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
position.value = {
|
|
|
|
x: ['top', 'bottom', 'none'].includes(props.fixedSide) ? newX : position.value.x,
|
|
|
|
y: ['left', 'right', 'none'].includes(props.fixedSide) ? newY : position.value.y
|
2025-02-12 08:50:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
const stopDrag = () => {
|
2025-02-12 08:50:52 +00:00
|
|
|
isDragging.value = false
|
|
|
|
}
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
// 事件监听
|
2025-02-12 08:50:52 +00:00
|
|
|
onMounted(() => {
|
2025-02-13 03:53:24 +00:00
|
|
|
document.addEventListener('mousemove', onDrag)
|
|
|
|
document.addEventListener('mouseup', stopDrag)
|
|
|
|
document.addEventListener('touchmove', onDrag)
|
|
|
|
document.addEventListener('touchend', stopDrag)
|
2025-02-12 08:50:52 +00:00
|
|
|
})
|
|
|
|
|
2025-02-13 03:53:24 +00:00
|
|
|
onUnmounted(() => {
|
|
|
|
document.removeEventListener('mousemove', onDrag)
|
|
|
|
document.removeEventListener('mouseup', stopDrag)
|
|
|
|
document.removeEventListener('touchmove', onDrag)
|
|
|
|
document.removeEventListener('touchend', stopDrag)
|
2025-02-12 08:50:52 +00:00
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
2025-02-13 03:53:24 +00:00
|
|
|
.draggable-container {
|
2025-02-12 08:50:52 +00:00
|
|
|
touch-action: none;
|
|
|
|
user-select: none;
|
2025-02-13 03:53:24 +00:00
|
|
|
z-index: 9999;
|
2025-02-12 08:50:52 +00:00
|
|
|
}
|
|
|
|
</style>
|