ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

在 Vue 3 中使用 VueUse usePointer 构建响应式指针追踪与手势识别

在 Vue 3 中使用 VueUse usePointer 构建响应式指针追踪与手势识别 在 Vue 3 中使用 VueUse usePointer 构建响应式指针追踪与手势识别【免费下载链接】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导读usePointer是 VueUse 在 Sensors传感器分类下提供的响应式指针状态组合式函数它将浏览器 Pointer Events API 的原始事件流封装为一系列可直接驱动模板与逻辑的响应式 Ref从而统一处理鼠标、触摸笔与触摸屏三种输入源。本文将以 usePointer 参考文档 为核心骨架完整讲解其组合式用法、组件用法与完整类型声明并结合本仓库中 gesture-circle.vue 的实战实现展示如何用usePointer完成画布指针轨迹绘制与画圆手势识别。usePointer 是什么usePointer属于 VueUse 的 Sensors 分类在 SKILL.md 中的描述为Reactive pointer state响应式指针状态其 Invocation 规则为AUTO在合适的场景下自动优先使用而不是手写自定义事件监听代码。与传统的mousemove监听不同它基于 W3C 的 Pointer Events 标准鼠标、触控笔pen和触摸touch都会产生统一结构的PointerEvent因此同一套代码可以跨鼠标与触屏设备工作无需为不同输入设备各写一套逻辑。在本仓库中vueuse/core通过 pnpm catalog 统一管理版本见 pnpm-workspace.yaml各应用与包如apps/stage-web、apps/stage-pocket、apps/stage-tamagotchi均直接依赖该 catalog 版本。基础用法一行代码拿到全局指针状态最基础的调用方式是不传任何选项此时usePointer会监听window上的指针事件返回的响应式状态即可直接在模板或逻辑中使用import { usePointer } from vueuse/core const { x, y, pressure, pointerType } usePointer()调用后x、y为指针在当前视口viewport中的坐标Refnumber随指针移动实时更新pressure为触点压力01鼠标在按下时通常为 0.5悬停时为 0pointerType标识当前输入设备类型取值为mouse、touch、pen或null。由于返回值全部是 Ref可以直接在模板中渲染也可以通过watch驱动副作用逻辑例如绘制轨迹、控制跟随元素位置等。组件用法UsePointer 无渲染组件usePointer同时提供了对应的无渲染组件UsePointer适合在模板中直接消费指针状态。默认情况下组件在window上追踪指针template UsePointer v-slot{ x, y } x: {{ x }} y: {{ y }} /UsePointer /templatev-slot中解构出的x、y即为响应式指针坐标任何模板表达式插值、绑定到style、v-bind等都会随指针移动自动更新。如果要在元素内部追踪局部坐标设置targetselftemplate UsePointer v-slot{ x, y } targetself x: {{ x }} y: {{ y }} /UsePointer /template此时x、y是相对于该元素自身的指针位置适合实现元素内的悬浮提示、局部视差等效果。核心 API 与完整类型声明usePointer的类型声明体现了它完整的能力边界包含状态结构、选项与返回值三部分。UsePointerState一次指针事件携带的全部信息export interface UsePointerState extends Position { pressure: number pointerId: number tiltX: number tiltY: number width: number height: number twist: number pointerType: PointerType | null }各字段含义字段类型说明x/y继承自Positionnumber指针坐标默认相对视口pressurenumber触点压力范围 01pointerIdnumber当前 PointerEvent 的标识符多触点场景用于区分触点tiltX/tiltYnumber触控笔在 X/Y 轴上的倾斜角度width/heightnumber接触面触点/笔尖的宽高单位 pxtwistnumber触控笔围绕自身轴的旋转角度pointerTypePointerType \| null输入设备类型mouse、touch、pen或null其中tiltX、tiltY、twist以及width、height是鼠标场景下通常无意义、而在触控笔/触摸屏绘画场景中非常有用的笔迹参数——这也是usePointer相比useMouse更适合绘图应用的关键差异。UsePointerOptions可配置项export interface UsePointerOptions extends ConfigurableWindow { /** * Pointer types that listen to. * * default [mouse, touch, pen] */ pointerTypes?: PointerType[] /** * Initial values */ initialValue?: MaybeRefPartialUsePointerState /** * default window */ target?: MaybeRefEventTarget | null | undefined | Document | Window }target事件监听目标默认window。可以传入 DOM 元素、document、window或这些值的 Ref。在仓库实战中可以看到usePointer({ target: canvasRef })这种传入元素 Ref 的用法将指针状态限定在画布范围内。pointerTypes需要监听的指针类型默认[mouse, touch, pen]全监听如果只想响应触控笔输入可传[pen]。initialValue初始值可传MaybeRefPartialUsePointerState用于 SSR 预渲染或首帧前提供合理的默认坐标避免出现0, 0或null造成的闪烁。ConfigurableWindow提供window覆盖选项便于 SSR 环境或测试环境注入伪造的 window。UsePointerReturn返回值结构export interface UsePointerReturn { pressure: Refnumber pointerId: Refnumber tiltX: Refnumber tiltY: Refnumber width: Refnumber height: Refnumber twist: Refnumber pointerType: RefPointerType | null x: Refnumber y: Refnumber isInside: ShallowRefboolean }与状态结构相比返回值额外提供了isInside: ShallowRefboolean用于表示指针当前是否位于target内部。当target为window时该值通常恒为true而当target指定为某个元素时它可以用于驱动悬停区域类交互。函数签名export declare function usePointer( options?: UsePointerOptions, ): UsePointerReturn仓库实战基于 usePointer 的画布手势识别本仓库的stage-web与stage-pocket两个应用中都包含一个开发工具页面 gesture-circle.vue对应移动端版本见 apps/stage-pocket/src/pages/devtools/gesture-circle.vue它以usePointer为核心实现了一个画圆手势检测器是学习usePointer真实工程用法的绝佳样例。将指针状态绑定到指定元素const canvasRef refHTMLCanvasElement() const { x, y } usePointer({ target: canvasRef }) const bounding useElementBounding(canvasContainerRef, { immediate: true, windowResize: true })与文档中的基础用法不同这里把target指定为动态创建的canvas元素的 Ref从而把指针坐标限定在画布交互区域。同时配合useElementBounding获取容器尺寸用于按容器大小初始化画布见 gesture-circle.vue 中监听容器挂载后创建 canvas 的逻辑。通过 watch 实时绘制指针轨迹watch([x, y], () { if (!canvasContext.value) return canvasContext.value.beginPath() canvasContext.value.fillStyle red // 20: padding left // 40: padding top // 40: header canvasContext.value.arc(x.value - 20, y.value - 40 - 40, 4, 0, 2 * Math.PI) canvasContext.value.closePath() canvasContext.value.fill() })watch([x, y], ...)在指针坐标变化时触发向 Canvas 2D 上下文绘制一个小圆点实现笔迹跟随指针的效果。代码中的坐标偏移注释padding 与 header表明这里的x、y是相对视口的坐标绘制时手动减去了页面布局偏移——这正是target为画布元素时需要注意的坐标换算问题。与节流/防抖组合降低数据量指针事件在拖动过程中触发频率极高直接驱动复杂逻辑可能造成不必要的计算。该页面用computed聚合坐标再通过 VueUse 的refThrottled与refDebounced控制采样频率const pointComputed computed(() { return { x: x.value, y: y.value, } }) const pointThrottled refThrottled(pointComputed, 50) const pointDebounced refDebounced(pointComputed, 50)pointThrottled50ms 节流用于收集手势采样点watch(pointThrottled, (point) { lastPoints.value.push(point) })见 gesture-circle.vuepointDebounced50ms 防抖用于在指针停顿后触发一次手势判定并清空采样点。用半径标准差判定画圆手势判定逻辑见 gesture-circle.vue充分利用了收集到的采样点先计算采样点的平均中心坐标再计算每个点到中心的半径及其标准差——若标准差小于阈值stdDev 20说明各点到中心的距离接近一致即可判定用户画了一个近似圆形isCircle置为true且count自增if (lastPoints.value.length minimumDataPoints) { const meanX lastPoints.value.reduce((sum, p) sum p.x, 0) / lastPoints.value.length const meanY lastPoints.value.reduce((sum, p) sum p.y, 0) / lastPoints.value.length const radii lastPoints.value.map(p Math.sqrt((p.x - meanX) ** 2 (p.y - meanY) ** 2), ) const avgRadius radii.reduce((sum, r) sum r, 0) / radii.length const variance radii.reduce((sum, r) sum (r - avgRadius) ** 2, 0) / radii.length const stdDev Math.sqrt(variance) if (stdDev 20) { count.value isCircle.value true } }模板中则实时渲染pointThrottled、pointDebounced、lastPoints、isCircle、count等状态方便在开发期直观验证指针追踪与手势识别是否工作正常见 gesture-circle.vue。这套模式usePointer采点 节流降频 几何判定可以直接迁移到本仓库移动端输入面板等需要手势的交互场景。事实上仓库在 MobileInteractiveArea.vue 中使用了同一 Sensors 分类下的usePointerSwipe实现滑动手势与usePointer形成了精确坐标 滑动手势的互补组合。与相关传感器函数的选型对比usePointer在 Sensors 分类中有若干近亲函数选型时注意区分函数定位适用场景usePointer响应式指针状态坐标、压力、笔迹参数、类型绘图、坐标跟随、手势轨迹采集useMouse仅响应式鼠标位置只需鼠标坐标、无需多设备统一usePointerSwipe基于 PointerEvents 的滑动方向检测横向滑动翻页、滑动解锁usePointerLock指针锁定 API游戏视角控制、自由旋转镜头具体每个函数的用法与类型声明可参见 references 目录如 usePointerSwipe.md、useMouse.md。使用要点与注意事项事件目标与坐标系target为window时x、y是视口坐标target为元素时仍需注意元素自身的滚动与页面偏移如仓库示例中手动减去 padding/header 偏移必要时配合useElementBounding计算元素左上角做换算。多输入统一默认同时监听mouse、touch、pen触屏设备上触摸即产生指针事件无需单独处理touchstart但若希望忽略某类输入如绘画应用中忽略鼠标通过pointerTypes过滤即可。高频事件与性能指针移动事件触发频繁对高频消费场景如每帧绘制、写存储建议配合refThrottled/refDebounced或useRafFn降频仓库的 gesture-circle 页面即为典型范例。SSR 环境usePointer依赖window与 Pointer Events类型声明中的ConfigurableWindow允许覆盖 window 以便测试服务端渲染时应通过initialValue提供初始坐标避免首帧闪烁。笔迹参数tiltX、tiltY、twist、width、height、pressure仅在pen与touch输入下才有实际意义实现压感笔迹时需要结合这些字段而非只依赖坐标。总结usePointer用极少的样板代码将浏览器 Pointer Events 封装为响应式状态是本仓库 Vue 应用中统一处理鼠标与触屏输入的推荐方案Invocation 为AUTO。从 usePointer 参考文档 的基础用法与类型声明出发配合 gesture-circle.vue 的实战实现你可以在自己的组件中快速落地指针轨迹绘制、悬停区域检测、绘画压感与画圆等手势识别能力同时保持代码简洁、可维护且性能可控。【免费下载链接】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),仅供参考
返回列表