vue-draggable-resizable-next
Version:
[Vue3 Component] Draggable and resizable component for vue3
181 lines (175 loc) • 4.63 kB
text/typescript
import { onMounted, onUnmounted, ref, watch, Ref } from 'vue'
type DragHandleFn = ({ x, y }: { x: number; y: number }) => void
interface Params {
dragStart?: DragHandleFn
dragEnd?: DragHandleFn
dragging?: DragHandleFn
x?: Ref<number>
y?: Ref<number>
autoUpdate?: boolean
unselect?: () => void
enable?: Ref<boolean>
}
export function useDraggableContainer(options: Params = {}) {
const {
dragStart,
dragEnd,
dragging,
autoUpdate = true,
unselect,
enable
} = options
let { x = ref(0), y = ref(0) } = options
let lstX: number = 0
let lstY: number = 0
const containerRef = ref<HTMLDivElement>()
const isDragging = ref(false)
const _unselect = (e: MouseEvent) => {
const target = e.target
if (!containerRef.value?.contains(<Node>target) && unselect) {
unselect()
}
}
const handleUp = (e: MouseEvent) => {
isDragging.value = false
document.documentElement.removeEventListener('mouseup', handleUp)
document.documentElement.removeEventListener('mousemove', handleDrag)
}
const handleDrag = (e: MouseEvent) => {
if (!(isDragging.value && containerRef.value)) return
const { pageX, pageY } = e
const deltaX = pageX - lstX
const deltaY = pageY - lstY
x.value = x.value + deltaX
y.value = y.value + deltaY
lstX = pageX
lstY = pageY
dragging && dragging({ x: x.value, y: y.value })
if (autoUpdate) {
containerRef.value.style.left = x + 'px'
containerRef.value.style.top = y + 'px'
}
}
const handleDown = (e: any) => {
if (!enable || enable.value) {
isDragging.value = true
lstX = e.pageX
lstY = e.pageY
document.documentElement.addEventListener('mousemove', handleDrag)
document.documentElement.addEventListener('mouseup', handleUp)
}
}
watch(isDragging, (cur, pre) => {
if (!pre && cur) {
dragStart && dragStart({ x: x.value, y: y.value })
} else {
dragEnd && dragEnd({ x: x.value, y: y.value })
}
})
onMounted(() => {
const el = containerRef.value
if (!el) return
el.style.left = x + 'px'
el.style.top = y + 'px'
document.documentElement.addEventListener('mousedown', _unselect)
el.addEventListener('mousedown', handleDown)
})
onUnmounted(() => {
if (!containerRef.value) return
document.documentElement.removeEventListener('mousedown', _unselect)
document.documentElement.removeEventListener('mouseup', handleUp)
document.documentElement.removeEventListener('mousemove', handleDrag)
})
return { containerRef }
}
export function useState<T>(initialState: T): [Ref<T>, (value: T) => void] {
const state = ref(initialState) as Ref<T>
const setState = (value: T) => {
state.value = value
}
return [state, setState]
}
export function watchProperties(props: any, emit: any) {
const [width, setWidth] = useState<number>(props.initW)
const [height, setHeight] = useState<number>(props.initH)
const [left, setLeft] = useState<number>(props.x)
const [top, setTop] = useState<number>(props.y)
const [enable, setEnable] = useState<boolean>(props.active)
const [dragging, setDragging] = useState<boolean>(false)
const [resizing, setResizing] = useState<boolean>(false)
watch(
width,
(newVal) => {
console.log('width', newVal)
emit('update:w', newVal)
},
{ immediate: true }
)
watch(
height,
(newVal) => {
emit('update:h', newVal)
},
{ immediate: true }
)
watch(top, (newVal) => {
emit('update:y', newVal)
})
watch(left, (newVal) => {
emit('update:x', newVal)
})
watch(enable, (newVal, oldVal) => {
emit('update:active', newVal)
if (!oldVal && newVal) {
emit('activated')
} else if (oldVal && !newVal) {
emit('deactivated')
}
})
watch(
() => props.w,
(newVal: number) => {
setWidth(newVal)
}
)
watch(
() => props.h,
(newVal: number) => {
setHeight(newVal)
}
)
watch(
() => props.x,
(newVal: number) => {
setLeft(newVal)
}
)
watch(
() => props.y,
(newVal: number) => {
setTop(newVal)
}
)
watch(
() => props.active,
(newVal: boolean) => {
setEnable(newVal)
}
)
return {
width,
setWidth,
height,
setHeight,
top,
setTop,
left,
setLeft,
enable,
setEnable,
dragging,
setDragging,
resizing,
setResizing
}
}