useDraggable 拖拽钩子函数文档
一、功能概述
useDraggable 是基于 Vue 3 的响应式拖拽钩子,用于实现 DOM 元素的可拖拽功能。它通过监听鼠标事件动态计算元素位置,并支持拖拽区域边界限制,适用于模态框、浮动窗口、自定义组件等需要拖拽交互的场景。
二、核心特性
响应式拖拽控制
通过draggable响应式参数动态开启/关闭拖拽功能,支持实时状态切换。边界限制
自动根据视口尺寸和目标元素尺寸计算拖拽范围,防止元素超出屏幕边界。多引用绑定
targetRef:绑定需要拖拽的目标元素(跟随鼠标移动的元素)dragRef:绑定触发拖拽的手柄元素(可选,若未传则默认目标元素自身可拖拽)
平滑定位
使用 CSStransform: translate实现元素定位,保证动画流畅性。
三、参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
targetRef | Ref<HTMLElement | undefined> | 目标元素引用(必填),需绑定实际 DOM 元素 |
dragRef | Ref<HTMLElement | undefined> | 拖拽手柄元素引用(可选),若未提供则默认通过目标元素自身触发拖拽 |
draggable | ComputedRef<boolean> | 拖拽功能开关(必填),通过计算属性动态控制是否允许拖拽 |
四、内部实现逻辑
1. 鼠标事件流程
Lexical error on line 2. Unrecognized text. graph LRA[鼠标按下(dragRef 或 targetRef --------------^
2. 边界计算规则
- 最小左边界:
minLeft = -targetLeft + offsetX(防止元素左边缘超出视口左边界) - 最大左边界:
maxLeft = clientWidth - targetLeft - targetWidth + offsetX(防止元素右边缘超出视口右边界) - 上下边界同理,基于视口高度和元素高度计算
五、使用示例
场景 1:基础拖拽(目标元素自身可拖拽)
vue
<template>
<div ref="targetRef" class="draggable-box">
按住我拖动
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useDraggable } from '.@eco-library/hooks';
const targetRef = ref<HTMLElement>();
const draggable = computed(() => true); // 始终允许拖拽
useDraggable(targetRef, undefined, draggable);
</script>
<style scoped>
.draggable-box {
position: absolute;
width: 200px;
height: 100px;
background: #4A90E2;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
}
</style>场景 2:带手柄的拖拽
vue
<template>
<div ref="targetRef" class="modal">
<div ref="dragRef" class="header">按住头部拖动</div>
<div class="content">模态框内容</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useDraggable } from '.@eco-library/hooks';
const targetRef = ref<HTMLElement>(); // 模态框主体
const dragRef = ref<HTMLElement>(); // 拖拽手柄(头部)
const draggable = computed(() => true); // 开启拖拽
useDraggable(targetRef, dragRef, draggable);
</script>场景 3:动态开关拖拽功能
vue
<template>
<div>
<button @click="toggleDraggable">
{{ isDraggable ? '禁用拖拽' : '启用拖拽' }}
</button>
<div ref="targetRef" class="draggable-box">
可拖拽元素(当前状态:{{ isDraggable ? '启用' : '禁用' }})
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useDraggable } from '.@eco-library/hooks';
const targetRef = ref<HTMLElement>();
const isDraggable = ref(true);
const draggable = computed(() => isDraggable.value);
useDraggable(targetRef, undefined, draggable);
</script>六、注意事项
元素定位要求
目标元素必须设置为position: absolute或position: fixed,否则拖拽功能无效。性能优化
- 避免在拖拽元素内部嵌套大量复杂子元素
- 若不需要边界限制,可修改内部边界计算逻辑(注释掉
Math.min/max部分)
移动端适配
目前仅支持鼠标事件,若需移动端手势拖拽,可扩展支持touchstart/touchmove/touchend事件。
七、扩展建议
1. 支持动画过渡
typescript
// 扩展钩子,添加过渡效果
export function useDraggableWithTransition(...args) {
const { targetRef, ...rest } = useDraggable(...args);
watchEffect(() => {
targetRef.value!.style.transition = 'transform 0.3s ease-out';
});
return { ...rest };
}2. 记录拖拽位置
typescript
// 扩展钩子,返回当前位置坐标
export function useDraggableWithPosition(...args) {
const { targetRef } = useDraggable(...args);
const position = computed(() => ({
x: parseFloat(targetRef.value!.style.transform.split(',')[0].split('(')[1]),
y: parseFloat(targetRef.value!.style.transform.split(',')[1])
}));
return { position, ...rest };
}八、依赖说明
- 核心依赖:Vue 3.x(
@vue/composition-api) - 类型支持:需手动声明参数类型(已包含 TypeScript 类型定义)
