airi 项目实战:VueUse useDraggable 完全指南——从基础拖拽到边界约束与自动滚动

发布时间:2026/9/10 11:58:23
airi 项目实战:VueUse useDraggable 完全指南——从基础拖拽到边界约束与自动滚动 airi 项目实战VueUse useDraggable 完全指南——从基础拖拽到边界约束与自动滚动【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi导读useDraggable是 VueUse 提供的一个「让元素可拖拽」的响应式组合式函数Composable它基于 Pointer Events 实现返回x、y坐标、拖拽状态与可直接绑定到元素的样式字符串。在本仓库airi中它被广泛应用于交互式舞台与工具面板例如 stage-web 的性能监控悬浮窗PerformanceOverlay.vue用它实现可拖动、可限界的调试面板博客中的 NMS/IoU 可视化演示nms-iou.vue则用它驱动目标检测框的交互。读完本文你将掌握useDraggable的全部选项、返回值、组件式用法并能结合源码示例在 Vue 3 项目中实现带边界约束、拖拽手柄与自动滚动的完整拖拽能力。本文以 useDraggable.md 为骨架结合仓库内真实调用代码展开讲解。在 airi 的 pnpm 工作区中vueuse/core由 catalog 统一管理版本为^14.4.0见 pnpm-workspace.yaml下述 API 均以该版本为准。基础用法让任意元素可拖拽useDraggable属于 VueUse 的 Elements 分类见 SKILL.md 中的函数表调用规则为 AUTO即场景适用时可直接使用。它接收两个参数目标元素引用与可选的配置对象。script setup langts import { useDraggable } from vueuse/core import { useTemplateRef } from vue const el useTemplateRef(el) // style 是一个辅助计算属性输出 left: ?px; top: ?px; const { x, y, style } useDraggable(el, { initialValue: { x: 40, y: 40 }, }) /script template div refel :stylestyle styleposition: fixed Drag me! I am at {{ x }}, {{ y }} /div /template要点目标元素通过useTemplateRefVue 3.5绑定refeluseDraggable内部会统一解包 ref / getter / 原始 DOM 元素参数类型为MaybeRefOrGetterHTMLElement | SVGElement | null | undefined即同时支持 SVG 元素。返回的style是一个ComputedRefstring直接绑定到元素的:style即可完成定位但元素本身必须处于可被left/top定位的状态如position: fixed或position: absolute。initialValue指定初始位置默认值为{ x: 0, y: 0 }。返回值五个响应式句柄useDraggable返回一个包含五个成员的对象UseDraggableReturn字段与语义如下表属性类型说明xRefnumber当前横坐标pxyRefnumber当前纵坐标pxpositionRef{x, y}当前坐标对象x/y的聚合视图isDraggingComputedRefboolean是否正在拖拽中styleComputedRefstringCSS 样式字符串形如left: ?px; top: ?px;x、y与position是同一份响应式状态的不同视图直接修改position.value { x, y }同样会驱动元素移动。这在需要「外部重置位置」或「拖拽结束后修正坐标」的场景中非常实用下文仓库示例会用到。isDragging可用于拖拽过程中给元素附加select-none、切换光标为grabbing等交互反馈。选项全解从坐标轴到事件阶段useDraggable的第二参数UseDraggableOptions提供了完整的拖拽行为控制逐一说明如下useDraggable(el, { // 初始位置默认: { x: 0, y: 0 } initialValue: { x: 40, y: 40 }, // 限制拖拽轴向x、y 或 both默认 axis: both, // 仅当直接点击目标元素本身时触发拖拽默认: false exact: false, // 阻止浏览器默认行为默认: false preventDefault: true, // 停止事件冒泡默认: false stopPropagation: false, // 使用捕获阶段监听事件默认: true capture: true, // 禁用拖拽默认: false disabled: false, // 允许触发拖拽的鼠标按键默认: [0] - 左键 buttons: [0], // 监听的指针类型默认: [mouse, touch, pen] pointerTypes: [mouse, touch, pen], // 自定义拖拽手柄元素默认: 目标元素本身 handle: handleRef, // 用于计算边界的容器元素默认: 无 containerElement: containerRef, // 绑定 pointermove/pointerup 事件的元素默认: window draggingElement: window, // 回调 onStart: (position, event) { // 返回 false 可阻止本次拖拽 }, onMove: (position, event) {}, onEnd: (position, event) {}, })各选项的详细语义axis限制拖拽方向。设为x时元素只能水平移动y时只能垂直移动默认both允许任意方向。exact默认false表示点击目标元素的任意子元素都能启动拖拽设为true后只有直接点击元素本身event.target el才生效适合目标内部存在按钮、链接等可点击子元素时避免误拖。preventDefault阻止浏览器的默认拖放行为如图片的原生拖拽。详见下文「阻止默认行为」。stopPropagation停止事件向父级传播避免拖拽动作被外层监听器捕获。capture默认true事件在捕获阶段派发能更早接管指针事件。disabled动态开关拖拽能力传入 ref / getter 时可在运行时响应式地启用或禁用。buttons允许触发拖拽的鼠标按键编号数组。语义与MouseEvent.button一致0主键通常为左键、1辅助键滚轮/中键、2次键右键、3第四键浏览器返回、4第五键浏览器前进。默认[0]即仅左键。pointerTypes参与拖拽的指针类型默认覆盖[mouse, touch, pen]即鼠标、触摸屏与手写笔都支持。handle指定拖拽手柄。设置后只有按住手柄区域才能拖动元素常用于「悬浮面板头部可拖、内容区不可拖」的交互。containerElement边界容器。设置后拖拽位置会基于容器计算并约束在内详见下文「容器边界约束」。draggingElementpointermove/pointerup事件的挂载目标默认window。在 iframe、滚动容器等特殊场景下可按需调整。onStart/onMove/onEnd拖拽生命周期回调参数为当前Position{ x, y }与原生PointerEvent。onStart返回false可阻止本次拖拽启动。阻止默认行为解决图片等元素的原生拖拽某些元素自带浏览器默认拖放行为典型的是img按住后会被浏览器拖走形成幽灵图。当你的可拖拽区域内部包含这类元素时需要显式开启preventDefaultimport { useDraggable } from vueuse/core const { x, y, style } useDraggable(el, { preventDefault: true, })从 Type Declarations 中可以看到preventDefault的类型是MaybeRefOrGetterboolean这意味着你可以传入 ref 或 getter 实现运行时动态切换而不仅是写死一个布尔值。容器边界约束与限制在可视区内containerElement把拖拽限制在容器内不设置containerElement时元素可以拖到任意位置无边界约束。传入容器引用后坐标将基于容器计算并自动约束在容器范围内import { useDraggable } from vueuse/core const { x, y } useDraggable(el, { containerElement: containerRef, })restrictInView拖拽不离开容器可视区除了containerElementVueUse 还提供了restrictInView选项开启后被拖拽元素在拖动过程中不会离开其容器的可见区域始终保持在容器的 viewport 之内默认false。当容器存在滚动裁剪、悬浮层需要始终「贴边可见」时非常有用。autoScroll靠近边缘自动滚动当容器存在滚动内容且被拖拽元素接近容器边缘时可以开启自动滚动const { x, y, style } useDraggable(el, { autoScroll: { speed: 2, // 自动滚动速度 margin: 30, // 触发自动滚动的边缘距离px direction: both, // 滚动方向: x | y | both }, })autoScroll同时接受布尔值与对象false默认完全关闭true使用内置默认值对象形式可精细控制——speed滚动速度默认2、margin触发阈值默认30即进入距边缘 30px 的带状区域即开始滚动、direction滚动方向默认both。speed与margin在类型声明中为MaybeRefOrGetternumber | Position即它们既可以是一个统一数值也可以分别传入{ x, y }两个方向的独立值并支持响应式引用。组件式用法UseDraggable 与位置持久化除了组合式 APIVueUse 还提供了对应的组件UseDraggable通过作用域插槽把x、y暴露给插槽内容template UseDraggable v-slot{ x, y } :initial-value{ x: 10, y: 10 } Drag me! I am at {{ x }}, {{ y }} /UseDraggable /template组件用法额外支持两个 propsstorageKey与storageType用于将元素位置持久化到浏览器存储刷新页面后位置自动恢复template UseDraggable storage-keyvueuse-draggable storage-typesession Refresh the page and I am still in the same position! /UseDraggable /templatestorageType取值为local或session对应localStorage与sessionStoragestorageKey是存储键名。这一能力适合需要记忆用户布局偏好的可拖拽面板。airi 仓库源码实证两个真实生产用例用例一可拖拽性能监控悬浮窗stage-web在 PerformanceOverlay.vue 中调试性能指标的悬浮窗完整使用了useDraggable的进阶能力组合const { width: boundaryWidth, height: boundaryHeight } useElementBounding(dragBoundary) const { width: overlayWidth, height: overlayHeight } useElementBounding(overlay) const { position, isDragging } useDraggable(overlay, { containerElement: dragBoundary, handle: dragHandle, preventDefault: true, restrictInView: true, onEnd() { positionInitialized.value true clampPosition() }, })值得借鉴的实现细节容器 手柄 阻止默认行为组合使用containerElement绑定了一个铺满安全区的dragBoundary容器见模板中的env(safe-area-inset-*)定位handle指向面板头部preventDefault: true配合restrictInView: true防止面板被拖出可视区。拖拽结束后主动钳制坐标onEnd中调用clampPosition()配合useElementBounding读取边界与面板尺寸用clamp把坐标收敛到[0, maxX]、[0, maxY]区间——即使拖拽过程中产生了越界值松手后也会被修正回容器内。基于position的单向绑定组件没有直接用style返回值而是用computed基于position.value生成left/top样式见 overlayPosition并提供了「重置位置」按钮直接覆写position.value说明position是可读写的响应式状态。交互反馈isDragging用于切换select-none与cursor-grabbing/cursor-grab光标与文档中isDragging的用途完全对应。用例二NMS/IoU 可视化演示中的可拖拽检测框博客组件在 nms-iou.vue 中两个目标检测框box1 / box2通过工厂函数批量接入拖拽function setupDraggableBox( box: RefDetection, el: RefHTMLElement | null, handle: RefHTMLElement | null, ) { useDraggable(el, { handle, initialValue: { x: box.value.x, y: box.value.y }, onMove(p) { box.value.x Math.round(p.x - containerBounding.left.value) box.value.y Math.round(p.y - containerBounding.top.value) }, }) }这个用例展示了onMove回调的典型用途拖拽过程中把指针坐标换算成相对容器的逻辑坐标减去容器左上角并写回业务状态box1/box2从而驱动 IoU 交并比的计算。handle指向检测框顶部的标签条object1HandleEl只有拖住标签条才能移动整个框——与文档中handle选项的语义完全吻合。两个用例分别覆盖了「拖拽 边界约束 位置修正」与「拖拽 坐标换算 业务状态联动」两种主流模式可作为实际开发的参考范式。完整类型声明速查以下是本文所有选项与返回值的类型定义原文节选自 useDraggable.md便于在 IDE 中对照阅读export interface UseDraggableOptions { /** 仅当直接点击元素本身时启动拖拽 default false */ exact?: MaybeRefOrGetterboolean /** 阻止事件默认行为 default false */ preventDefault?: MaybeRefOrGetterboolean /** 阻止事件传播 default false */ stopPropagation?: MaybeRefOrGetterboolean /** 是否在捕获阶段派发事件 default true */ capture?: boolean /** 挂载 pointermove / pointerup 事件的元素 default window */ draggingElement?: MaybeRefOrGetterHTMLElement | SVGElement | Window | Document | null | undefined /** 用于计算边界的元素未设置时使用事件目标 */ containerElement?: MaybeRefOrGetterHTMLElement | SVGElement | null | undefined /** 触发拖拽的手柄元素 default target */ handle?: MaybeRefOrGetterHTMLElement | SVGElement | null | undefined /** 监听的指针类型 default [mouse, touch, pen] */ pointerTypes?: PointerType[] /** 元素初始位置 default { x: 0, y: 0 } */ initialValue?: MaybeRefOrGetterPosition /** 拖拽启动回调返回 false 阻止拖拽 */ onStart?: (position: Position, event: PointerEvent) void | false /** 拖拽过程中回调 */ onMove?: (position: Position, event: PointerEvent) void /** 拖拽结束回调 */ onEnd?: (position: Position, event: PointerEvent) void /** 拖拽轴向 default both */ axis?: x | y | both /** 禁用拖拽 default false */ disabled?: MaybeRefOrGetterboolean /** 允许触发拖拽的鼠标按键 default [0] */ buttons?: MaybeRefOrGetternumber[] /** 是否限制在容器可视区内 default false */ restrictInView?: MaybeRefOrGetterboolean /** 靠近边缘时是否自动滚动 default false */ autoScroll?: MaybeRefOrGetterboolean | { speed?: MaybeRefOrGetternumber | Position // 默认 2 margin?: MaybeRefOrGetternumber | Position // 默认 30 direction?: x | y | both // 默认 both } } export interface UseDraggableReturn { x: Refnumber y: Refnumber position: RefPosition isDragging: ComputedRefboolean style: ComputedRefstring }小结useDraggable以极少的样板代码封装了 Pointer Events 的全套拖拽逻辑通过initialValue/axis/handle/containerElement/restrictInView/autoScroll等选项覆盖从基础拖动到边界约束、手柄限定与自动滚动的全部常见需求通过onStart/onMove/onEnd回调与业务状态联动并提供position、isDragging等响应式句柄供外部读写。airi 仓库中的 PerformanceOverlay.vue 与 nms-iou.vue 是理解其高级用法的绝佳范本——前者展示了「容器 手柄 可视区限制 结束钳位」的组合拳后者展示了「拖拽坐标换算 业务状态回写」的联动模式。在开发 Vue 3 应用时遇到任何「让元素可拖」的需求优先考虑useDraggable在 vueuse-functions 技能中的调用规则为 AUTO既能保持代码简洁也能获得经过充分测试的跨端鼠标 / 触摸 / 手写笔兼容性。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询