import type { Ref } from 'vue';
|
import { computed, ref } from 'vue';
|
|
export type UseDragOptions = {
|
/**
|
* 拖拽手柄元素的选择器
|
*/
|
handle?: string | HTMLElement | Ref<HTMLElement>;
|
};
|
|
const getHandleElement = (handle: string | HTMLElement | Ref<HTMLElement>) => {
|
if (!handle) return null;
|
if (typeof handle === 'string') {
|
return document.querySelector(handle);
|
} else if (handle instanceof HTMLElement) {
|
return handle;
|
} else if (typeof handle === 'object' && 'value' in handle) {
|
return handle.value;
|
}
|
return null;
|
};
|
|
export const useDrag = (options: UseDragOptions = {}) => {
|
const { handle } = options;
|
const isDragging = ref(false);
|
const startPos = ref({ x: 0, y: 0 });
|
const offset = ref({ x: 0, y: 0 });
|
const startDrag = (e: MouseEvent) => {
|
// 如果设置了handle,则判断事件源是否是handle元素或其子元素
|
if (handle) {
|
const handleElement = getHandleElement(handle);
|
if (!handleElement?.contains(e.target as Node)) {
|
return;
|
}
|
}
|
isDragging.value = true;
|
startPos.value = {
|
x: e.clientX - offset.value.x,
|
y: e.clientY - offset.value.y,
|
};
|
|
const handleMouseMove = (e: MouseEvent) => {
|
if (!isDragging.value) return;
|
|
offset.value = {
|
x: e.clientX - startPos.value.x,
|
y: e.clientY - startPos.value.y,
|
};
|
};
|
|
const handleMouseUp = () => {
|
isDragging.value = false;
|
document.removeEventListener('mousemove', handleMouseMove);
|
document.removeEventListener('mouseup', handleMouseUp);
|
};
|
|
document.addEventListener('mousemove', handleMouseMove);
|
document.addEventListener('mouseup', handleMouseUp);
|
};
|
const handleStyle = computed(() => {
|
return handle
|
? {
|
cursor: isDragging.value ? 'grabbing' : 'grab',
|
}
|
: {};
|
});
|
|
const style = computed(
|
() =>
|
({
|
position: 'absolute',
|
transform: `translate(${offset.value.x}px, ${offset.value.y}px)`,
|
...(handle
|
? {}
|
: {
|
cursor: isDragging.value ? 'grabbing' : 'grab',
|
}),
|
} as any)
|
);
|
|
return {
|
isDragging,
|
startDrag,
|
style,
|
handleStyle,
|
};
|
};
|