react-use 的 usePinchZoom 传感器 Hook 实战:用 Pointer Events 检测双指捏合缩放
react-use 的 usePinchZoom 传感器 Hook 实战用 Pointer Events 检测双指捏合缩放【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-useusePinchZoom是 react-use当前仓库版本 17.6.1见 package.json提供的一个传感器Sensor类 Hook它监听目标元素上的指针触摸事件计算两根手指之间的水平距离变化从而告知调用方用户当前正在放大ZOOMING_IN还是缩小ZOOMING_OUT。读完本文你将掌握该 Hook 的完整 API、源码级实现原理并能够直接写出基于它实现的可运行图片缩放组件。一、usePinchZoom 是什么react-use 将 Hooks 分为多个类别其中传感器类 Hook 的定义在 docs/Sensors.md 中给出它们监听某种接口的变化并强制组件携带最新状态重新渲染。usePinchZoom正是这一类中的一员它被列在 README.md 的 Sensors 分组下其定位是追踪 pointer 事件检测捏合缩放是放大还是缩小。与基于传统touchstart/touchmove的写法不同该 Hook 采用标准Pointer Eventspointerdown、pointermove、pointerup等实现因此对鼠标、触摸屏、触控笔等统一抽象。它在仓库中通过 src/index.ts 以export { default as usePinchZoom } from ./usePinchZoom对外导出首次引入记录在 CHANGELOG.md。二、安装与导入从react-use包中导入即可import { usePinchZoom } from react-use;仓库内部完整实现位于 src/usePinchZoom.ts类型声明与默认导出均来自该文件。如果需要在代码中引用方向枚举ZoomState从仓库源码看它定义在 src/usePinchZoom.ts 中且并未从 src/index.ts 入口导出入口仅导出 Hook 本身仓库内的演示 stories/usePinchZoom.story.tsx 是直接import { ZoomState } from ../src/usePinchZoom引用的你可以参照这一方式。三、API 签名与返回值从 src/usePinchZoom.ts 的源码可以归纳出完整的 API 形态项目说明函数签名usePinchZoom(ref: RefObjectHTMLElement)参数ref绑定手势监听的目标 DOM 元素的 ref必须是HTMLElement如div、img的容器返回值{ zoomingState, pinchState }zoomingState缩放方向值为ZoomState.ZOOMING_IN、ZoomState.ZOOMING_OUT或初始的nullpinchState当前两根指针之间的水平距离像素无手势时为0其中ZoomState枚举定义为src/usePinchZoom.tsexport enum ZoomState { ZOOMING_IN ZOOMING_IN, ZOOMING_OUT ZOOMING_OUT, } export type ZoomStateType ZoomState.ZOOMING_IN | ZoomState.ZOOMING_OUT;返回值在无手势时由源码第 104-106 行保证为稳定结构{ zoomingState: null, pinchState: 0 }一旦检测到方向变化zoomingState即为枚举值pinchState为对应pointermove事件时刻的两指水平间距Math.abs(clientX1 - clientX2)单位为像素。四、基础用法文档示例完整版原文档 docs/usePinchZoom.md 给出了最核心的用法——把缩放方向变化映射为图片的zoom样式。下面是对其完整继承并补充注释、修正依赖数组的版本import { useEffect, useRef, useState } from react; import { usePinchZoom } from react-use; const Demo () { const [scale, setState] useState(1); const scaleRef useRef(); const { zoomingState, pinchState } usePinchZoom(scaleRef); useEffect(() { if (zoomingState ZOOMING_IN) { // 手指距离增大 → 放大 setState((s) s 0.1); } else if (zoomingState ZOOMING_OUT) { // 手指距离减小 → 缩小 setState((s) s - 0.1); } }, [zoomingState, pinchState]); return ( div ref{scaleRef} img srchttps://www.olympus-imaging.co.in/content/000107506.jpg style{{ zoom: scale }} altscale img / /div ); }; export default Demo;注意字符串值原文档示例中写的是ZOOM_IN/ZOOM_OUT而仓库源码实际产生的方向值是ZOOMING_IN/ZOOMING_OUT见 src/usePinchZoom.ts。直接复制原文档字符串将永远匹配不到分支建议按上面代码使用枚举对应的字符串值或像仓库演示那样导入ZoomState枚举进行比较。这是使用该 Hook 时最容易踩的坑。上面的useEffect依赖同时包含zoomingState与pinchState与仓库演示 stories/usePinchZoom.story.tsx 保持一致用函数式更新setState((s) s 0.1)可避免连续缩放时基于过期 state 计算的问题。五、工作原理源码级解析理解实现有助于你判断它在真实项目中的行为边界。核心逻辑全部在 src/usePinchZoom.ts 中关键结构如下。5.1 缓存结构与手势状态export type CacheRef { prevDiff: number; evCache: ArrayPointerEvent; };Hook 内部用useMemo维护一个cacheRefsrc/usePinchZoom.ts初始值为{ evCache: [], prevDiff: -1 }仅当ref.current引用变化时才重建evCache当前仍处于按下状态的指针事件缓存用于跟踪两根活动指针prevDiff上一次pointermove时两指的水平间距初始为-1表示尚无有效基准。5.2 三个核心事件处理函数pointerdown_handlersrc/usePinchZoom.tspointerdown时将事件对象推入evCache表示这根手指已按下。pointermove_handlersrc/usePinchZoom.ts这是判定缩放方向的核心先在缓存中找到与该pointerId匹配的记录并更新为最新事件当evCache.length 2恰好两根手指按下时计算当前水平距离curDiff Math.abs(clientX1 - clientX2)只有当prevDiff 0即已有上一次基准时才做比较curDiff prevDiff置为ZOOMING_INcurDiff prevDiff置为ZOOMING_OUT无论是否触发状态更新都会把curDiff写入prevDiff作为下一次比较的基准。pointerup_handlersrc/usePinchZoom.ts指针抬起或cancel/out/leave时调用remove_event把该指针从缓存中移除当剩余指针不足两根时把prevDiff重置为-1等待下一次完整手势重新建立基准。5.3 事件绑定Hook 通过useEffectsrc/usePinchZoom.ts在ref.current上一次性挂载ref.current.onpointerdown pointerdown_handler; ref.current.onpointermove pointermove_handler; ref.current.onpointerup pointerup_handler; ref.current.onpointercancel pointerup_handler; ref.current.onpointerout pointerup_handler; ref.current.onpointerleave pointerup_handler;它直接赋值onpointer*属性而非addEventListener依赖数组为[ref?.current]因此 ref 变化时会重新绑定组件卸载时由 React 一并清理 DOM 属性上的处理函数。5.4 数据流小结pointerdown缓存指针→ pointermove两指时算水平距离并对比 prevDiff → 距离增大 → setZoomingState([ZOOMING_IN, curDiff]) → 距离减小 → setZoomingState([ZOOMING_OUT, curDiff]) → pointerup/cancel/out/leave移除指针指针不足两根则重置基准六、实现细节与使用注意事项结合源码使用该 Hook 时有以下几点值得注意均为从源码结构可确认或可推断的事实只计算水平方向距离实现中使用的是Math.abs(evCache[0].clientX - evCache[1].clientX)src/usePinchZoom.ts即一维水平距离而非两指间的真实欧氏距离。垂直方向的捏合、斜向捏合的距离变化不会被完整度量。需要全方向距离的场景例如用Math.hypot(dx, dy)计算对角线长度需要自行扩展。只有恰好两根指针才判定evCache.length 2是进入判定分支的硬条件。若用户捏合过程中第三根手指落下缓存长度变为 3判定暂时失效直到某根手指抬起回到 2 根才恢复。这不是缺陷而是双指手势语义下的合理取舍。首次 move 不产生状态由于prevDiff初始为-1双指按下后的第一次pointermove只建立基准、不触发zoomingState更新从第二次 move 起才会比较并返回方向。pinchState在无手势时固定为0src/usePinchZoom.ts。不干预浏览器默认手势从源码看Hook 没有设置touch-action: none也未调用preventDefault()。在触屏浏览器中双指捏合可能同时触发页面自身的缩放/滚动行为两者叠加时体验可能受影响。如果你的目标场景需要独占手势可在目标元素 CSS 上自行加上touch-action: none。状态与样式解耦Hook 只报告方向与距离不替你执行任何缩放变换。实际缩放多少本例每步 0.1、用什么样式zoom或transform: scale()完全由调用方决定这保证了 Hook 的通用性。七、进阶实战带边界约束的图片缩放组件把上面的思路整合成一个更完整的组件加入缩放范围限制避免无限放大或缩到看不见import { useEffect, useRef, useState } from react; import { usePinchZoom } from react-use; const MIN_SCALE 0.5; const MAX_SCALE 3; const STEP 0.1; const PinchZoomImage ({ src }) { const [scale, setScale] useState(1); const ref useRef(null); const { zoomingState } usePinchZoom(ref); useEffect(() { setScale((prev) { if (zoomingState ZOOMING_IN) { return Math.min(prev STEP, MAX_SCALE); } if (zoomingState ZOOMING_OUT) { return Math.max(prev - STEP, MIN_SCALE); } return prev; }); }, [zoomingState]); return ( div ref{ref} style{{ touchAction: none, overflow: hidden }} img src{src} altpinch zoom target style{{ zoom: scale, transition: zoom 80ms linear, }} / /div ); }; export default PinchZoomImage;要点说明touchAction: none让浏览器把手势事件完全交给元素对应上文注意事项 4用Math.min/Math.max把缩放值钳制在[0.5, 3]防止越界加入轻微过渡动画可让缩放过程更平滑但注意zoom属性在现代浏览器中的过渡支持以 Chromium 系为主跨浏览器时可改用transform: scale(scale)并设置transformOrigin: 0 0。八、本地运行与演示仓库提供了该 Hook 的 Storybook 演示位于 stories/usePinchZoom.story.tsx其中Demo组件展示了与本文第四节相同的缩放逻辑并挂载在Sensors/usePinchZoom分组下包含Docs渲染 docs/usePinchZoom.md 原文和Default可交互演示两个 story。在仓库根目录执行以下命令即可本地启动yarn install yarn storybookStorybook 默认运行在http://localhost:6008端口由 package.json 中的storybook: start-storybook -p 6008脚本指定进入后导航到 Sensors → usePinchZoom 的 Default 页面即可在支持触摸的屏幕上体验双指缩放或使用浏览器开发者工具的触摸模拟进行调试。九、小结usePinchZoom是一个轻量、聚焦的传感器 Hook输入一个元素 ref输出捏合方向 水平间距把最繁琐的 pointer 事件缓存、距离比较与基准重置逻辑封装在约 100 行的实现内src/usePinchZoom.ts。它在图片查看器、地图缩放、幻灯片手势等双指捏合交互场景中即插即用同时也要记住它的两个天然边界——只度量水平距离、只处理恰好两根指针复杂手势需求需要在此基础上自行扩展。【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Halcon机器视觉入门:从模板匹配到尺寸测量与手眼标定

Halcon机器视觉入门:从模板匹配到尺寸测量与手眼标定

我第一次接触Halcon的时候,其实挺懵的。那时候项目周期紧,需要上视觉定位,网上资料又不多,手里的Demo跑起来是一回事,真正放进产线稳定跑是另一回事。后来啃了几年,踩过不少坑,才慢慢把Halcon这…

2026/9/19 9:31:15 阅读更多 →
Textual ListView 指南:用 Python 构建可键盘导航的垂直列表界面

Textual ListView 指南:用 Python 构建可键盘导航的垂直列表界面

Textual ListView 指南:用 Python 构建可键盘导航的垂直列表界面 【免费下载链接】textual The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser. 项目…

2026/9/19 9:31:15 阅读更多 →
Kalibr多板同帧标定:原图复制+遮挡生成虚拟帧,精度0.2像素

Kalibr多板同帧标定:原图复制+遮挡生成虚拟帧,精度0.2像素

做相机标定的朋友,八成遇到过这种尴尬:采集的时候没注意,回看数据发现一帧画面里同时进了七八块标定板。常规做法是删掉这种“脏帧”重新拍,但如果项目已经收尾、现场设备都拆了,或者那组数据本身就是拿广角相机一次拍…

2026/9/19 9:31:15 阅读更多 →

最新新闻

MATLAB实现物理信息神经网络PINN的故障诊断实战指南

MATLAB实现物理信息神经网络PINN的故障诊断实战指南

简介:面向工业故障诊断与智能运维场景,基于物理约束神经网络(PINN)的MATLAB分类预测实现,适合掌握一定编程和机器学习基础、希望将物理机理与数据驱动结合的研究人员、工程师和技术人员,可应用于航空航天、…

2026/9/20 20:32:00 阅读更多 →
高中数学知识点全总结:从函数导数到解析几何的系统复习框架

高中数学知识点全总结:从函数导数到解析几何的系统复习框架

简介:文档为高中数学知识点全总结,面向高中学生及备考考生,系统梳理函数与导数、平面向量与三角函数、数列、空间向量与立体几何、概率统计、解析几何、参数方程等高考核心板块,并附有易错点与学习策略指导。包体仅含1个doc文件&a…

2026/9/20 20:32:00 阅读更多 →
CAP分子病理检查清单解读:从认证标准到实验室落地实践

CAP分子病理检查清单解读:从认证标准到实验室落地实践

简介:美国病理学家学院(CAP)认证项目中的分子病理学检查清单,是面向分子病理学实验室的现场审核标准,适合准备参加CAP认证、迎接复审或完善质量体系的实验室人员阅读。此份PDF为2017年8月21日修订版本,压缩…

2026/9/20 20:32:00 阅读更多 →
母爛漫二周目全CG收集攻略:路线解锁与选项分支详解

母爛漫二周目全CG收集攻略:路线解锁与选项分支详解

简介:《母爛漫》二周目全CG图文攻略是一份面向该游戏玩家、以快速收集全CG与结局为导向的实用文档。内容完整覆盖茧子与羽华两条互斥主线,以及各自GE/NE共四种结局的达成条件与判定逻辑,并针对补心进度压制、关键事件触发时机、SL刷取转盘事件…

2026/9/20 20:32:00 阅读更多 →
固体物理总复习:模块化重组阎守胜教材,攻克能带与晶格振动

固体物理总复习:模块化重组阎守胜教材,攻克能带与晶格振动

简介:《固体物理总复习(阎守胜)》是一份面向物理专业本科生及备考人员的固体物理核心知识梳理PDF,围绕阎守胜教材重难点提炼出晶体结构、原胞与基矢、布拉伐点阵、典型晶格类型、配位数与致密度、晶列晶面、七大晶系与14种布拉伐格子、倒易点阵等专题&am…

2026/9/20 20:32:00 阅读更多 →
InvenTree 库存管理实践指南:从Docker部署到条码出入库

InvenTree 库存管理实践指南:从Docker部署到条码出入库

InvenTree 库存管理实践指南:从Docker部署到条码出入库 【免费下载链接】InvenTree Open Source Inventory Management System 项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree InvenTree 是一款完全开源的库存管理与物料追踪系统,适…

2026/9/20 20:31:00 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →