Preact Moveable 实战指南:用 Preact 组件实现拖拽、缩放、旋转、扭曲与编组变换

发布时间:2026/10/10 15:49:07
Preact Moveable 实战指南:用 Preact 组件实现拖拽、缩放、旋转、扭曲与编组变换 前端UI组件【免费下载链接】moveableMoveable! Draggable! Resizable! Scalable! Rotatable! Warpable! Pinchable! Groupable! Snappable!项目地址https://gitcode.com/gh_mirrors/mo/moveable点击查看免费下载本文基于开源仓库中的preact-moveable包packages/preact-moveable官方 README 与配套的 Groupable 指南编写系统讲解如何在 Preact 应用中通过一个Moveable组件为任意 DOM 目标包括 SVG 元素附加拖拽Draggable、调整尺寸Resizable、缩放Scalable、旋转Rotatable、扭曲Warpable、捏合Pinchable、编组Groupable与吸附Snappable等交互能力。读完本文你将掌握 preact-moveable 的完整安装与接入方式、全部内置能力的 Props 与事件回调用法、Groupable 编组变换的实操模式以及该组件封装 react-moveable的底层架构原理。一、Preact Moveable 是什么核心能力总览preact-moveable是一个基于 TypeScript 编写的 Preact 组件其定位在官方 README 中描述得非常明确A Preact Component that create Moveable, Draggable, Resizable, Scalable, Rotatable, Warpable, Pinchable, Groupable, Snappable即一个可以同时让目标元素具备移动、缩放、旋转、扭曲、捏合、编组、吸附等全套交互能力的 Preact 组件。从官方 README 的 Features 一节可以完整归纳出以下能力清单Draggable可拖拽拖拽并移动目标元素。Resizable可调整尺寸增加或减少目标元素的宽度width与高度height。Scalable可缩放通过 transform 的 scale 对目标元素的 x/y 进行缩放。Rotatable可旋转旋转目标元素。Warpable可扭曲对目标元素进行 warp扭曲/形变本质是 4x4 矩阵的乘法变换。Pinchable可捏合在触控场景下将 draggable、resizable、scalable、rotatable 组合为多指捏合操作。Groupable可编组多个目标元素以组为单位一起进行 draggable、resizable、scalable、rotatable。Snappable可吸附吸附到参考线guideline。OriginDraggable拖拽目标元素的变换原点origin。Clippable可裁剪对目标元素进行裁剪clip。Roundable可圆角显示并拖拽或双击修改 border-radius。除此之外官方 README 还明确列出了三项平台级能力支持 SVG 元素svg、path、line、ellipse、g、rect 等支持主流浏览器支持 3D 变换3d transform。这意味着同一个组件既可以操作普通 HTML 元素也可以操作SVG场景下的path、line、ellipse、g、rect等元素并可配合matrix3d实现三维透视变换这一点在 Warpable 的示例中体现得最为直观。二、源码架构Preact 对 react-moveable 的轻量封装在深入使用之前先理解它的实现原理能帮助你更好地判断何时选择 preact-moveable。从仓库源码结构看preact-moveable并不是从零实现的一套交互引擎而是对react-moveable的 Preact 化封装。核心证据在 Moveable.ts 中全文只有寥寥几行import Moveable from react-moveable; import Preact from preact; import { PreactMoveableInterface } from ./types; export default Moveable as any as new (...args: any[]) PreactMoveableInterface;也就是说preact-moveable直接复用react-moveable的组件实现并通过类型断言把它声明为一个 Preact 组件构造器从而让 Preact 的渲染层可以正常驱动它。这一点在 package.json 的依赖声明中也有印证react-moveable: ~0.56.0同时依赖preact-css-styled来承载组件样式。类型层面的衔接定义在 types.ts 中import { MoveableProps, MoveableState, MoveableInterface } from react-moveable/types; import { Component } from preact; export type PreactMoveableInterface { [key in Excludekeyof MoveableInterface, setState]: MoveableInterface[key] } ComponentMoveableProps, MoveableState;它把react-moveable的MoveableInterface与 Preact 的Component类型合并排除了setState该能力由 Preact 的 Component 提供从而保证在 Preact 中调用moveable.updateRect()、moveable.isMoveableElement()等实例方法时拥有完整的类型提示。而入口文件 index.ts 会同时export * from react-moveable/types与export * from ./types因此在使用端你可以直接从preact-moveable导入OnDrag、OnResize、OnScale、OnRotate等全部事件类型定义。仓库内的官方 demo 入口 App.tsx 正是按这种方式使用的import { h, Component } from preact; import Moveable, { MoveableInterface } from ../preact-moveable;从源码结构可以看出选择 preact-moveable 的好处是你可以完全用 Preact 的组件模型props、ref、生命周期来使用 Moveable 的全部能力而底层交互引擎保持与 react-moveable 完全一致。三、安装与引入官方 README 给出的安装命令只有一条$ npm i preact-moveable安装完成后在 Preact 应用中按如下方式引入import Moveable from preact-moveable;如果需要用到事件参数类型如OnDrag、OnResize同样从preact-moveable导入即可因为入口文件已经重新导出了 react-moveable 的全部类型定义。需要说明的是preact-moveable的 peer 环境是 Preact 10.x见 package.json 中preact: ^10.0.0请确保你的项目 Preact 版本与之兼容。四、快速上手一个完整可运行的 Moveable 示例官方 README 的 How to use 一节给出了一个覆盖全部主要能力的最小完整示例下面完整复现并补充逐行注释。将其放入任意 Preact 组件的render()中即可运行import Moveable from preact-moveable; render() { return ( Moveable target{document.querySelector(.target)} container{null} origin{true} /* Resize, Scale event edges */ edge{false} /* draggable */ draggable{true} throttleDrag{0} onDragStart{({ target, clientX, clientY }) { console.log(onDragStart, target); }} onDrag{({ target, beforeDelta, beforeDist, left, top, right, bottom, delta, dist, transform, clientX, clientY, }: OnDrag) { console.log(onDrag left, top, left, top); // target!.style.left ${left}px; // target!.style.top ${top}px; console.log(onDrag translate, dist); target!.style.transform transform; }} onDragEnd{({ target, isDrag, clientX, clientY }) { console.log(onDragEnd, target, isDrag); }} /* When resize or scale, keeps a ratio of the width, height. */ keepRatio{true} /* resizable*/ /* Only one of resizable, scalable, warpable can be used. */ resizable{true} throttleResize{0} onResizeStart{({ target , clientX, clientY}) { console.log(onResizeStart, target); }} onResize{({ target, width, height, dist, delta, clientX, clientY, }: OnResize) { console.log(onResize, target); delta[0] (target!.style.width ${width}px); delta[1] (target!.style.height ${height}px); }} onResizeEnd{({ target, isDrag, clientX, clientY }) { console.log(onResizeEnd, target, isDrag); }} /* scalable */ /* Only one of resizable, scalable, warpable can be used. */ scalable{true} throttleScale{0} onScaleStart{({ target, clientX, clientY }) { console.log(onScaleStart, target); }} onScale{({ target, scale, dist, delta, transform, clientX, clientY, }: OnScale) { console.log(onScale scale, scale); target!.style.transform transform; }} onScaleEnd{({ target, isDrag, clientX, clientY }) { console.log(onScaleEnd, target, isDrag); }} /* rotatable */ rotatable{true} throttleRotate{0} onRotateStart{({ target, clientX, clientY }) { console.log(onRotateStart, target); }} onRotate{({ target, delta, dist, transform, clientX, clientY, }: onRotate) { console.log(onRotate, dist); target!.style.transform transform; }} onRotateEnd{({ target, isDrag, clientX, clientY }) { console.log(onRotateEnd, target, isDrag); }} /* warpable */ /* Only one of resizable, scalable, warpable can be used. */ /* this.matrix [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ] */ warpable{true} onWarpStart{({ target, clientX, clientY }) { console.log(onWarpStart, target); }} onWarp{({ target, clientX, clientY, delta, dist, multiply, transform, }) { console.log(onWarp, target); // target.style.transform transform; this.matrix multiply(this.matrix, delta); target.style.transform matrix3d(${this.matrix.join(,)}); }} onWarpEnd{({ target, isDrag, clientX, clientY }) { console.log(onWarpEnd, target, isDrag); }} // Enabling pinchable lets you use events that // can be used in draggable, resizable, scalable, and rotateable. pinchable{true} onPinchStart{({ target, clientX, clientY, datas }) { // pinchStart event occur before dragStart, rotateStart, scaleStart, resizeStart console.log(onPinchStart); }} onPinch{({ target, clientX, clientY, datas }) { // pinch event occur before drag, rotate, scale, resize console.log(onPinch); }} onPinchEnd{({ isDrag, target, clientX, clientY, datas }) { // pinchEnd event occur before dragEnd, rotateEnd, scaleEnd, resizeEnd console.log(onPinchEnd); }} / ) }这个示例直接对应官方演示中拖拽能力的实际效果其界面形态可参考仓库中的 draggable.gif五、逐项能力详解Props、事件与回调上一节的示例代码中实际出现了三类元素开关型 Props如draggable、resizable、rotatable、pinchable、节流型 Props如throttleDrag、throttleResize、throttleRotate、throttleScale以及生命周期事件每个能力都有Start/ 进行中 /End三个阶段的回调。下面逐一展开。5.1 Draggable拖拽移动开关属性为draggable{true}节流参数throttleDrag默认示例为0即不节流设置大于 0 的值可以按像素步进约束移动距离。三个事件回调的职责非常清晰onDragStart拖拽开始参数中可拿到target、clientX、clientYonDrag拖拽进行中参数最为丰富包含beforeDelta/beforeDist本次拖拽开始前的累计增量/累计距离left/top/right/bottom目标元素当前的几何位置delta/dist本帧的增量/总距离transformMoveable 计算好的变换字符串clientX/clientY指针坐标。官方示例中给出了两种应用方式注释掉的target.style.left left px/target.style.top top px基于 left/top 定位以及推荐的target!.style.transform transform基于 transform 定位。onDragEnd拖拽结束isDrag用于判断本次是否发生了有效拖拽而非单纯点击。5.2 Resizable调整宽高开关属性为resizable{true}节流参数throttleResize。注意官方注释明确resizable、scalable、warpable 三者同一时间只能启用其中一个。onResize回调的参数包括width、height、dist、delta、clientX、clientY官方示例的应用方式非常经典onResize{({ target, width, height, delta }) { delta[0] (target!.style.width ${width}px); delta[1] (target!.style.height ${height}px); }}即用delta[0]宽度是否有变化和delta[1]高度是否有变化作为门控只有对应方向发生变化时才写回样式避免无意义的重复赋值。5.3 Scalable缩放开关属性为scalable{true}节流参数throttleScale。同样受resizable、scalable、warpable 三选一约束。onScale回调的参数为scale本次缩放比例如[1.2, 1.2]、dist、delta、transform等官方示例直接把transform写回目标onScale{({ target, scale, dist, delta, transform }) { target!.style.transform transform; }}5.4 Rotatable旋转开关属性为rotatable{true}节流参数throttleRotate。onRotate回调中比较关键的是dist总旋转角度单位为度与transform示例同样以target!.style.transform transform方式应用旋转结果。5.5 Warpable矩阵扭曲开关属性为warpable{true}同样受三选一约束。它是所有能力中最能体现 Moveable 底层矩阵运算能力的一项你需要自行维护一个 4x4 的单位矩阵示例代码开头注释中的this.matrix在onWarp回调中用参数提供的multiply函数把当前矩阵与本次增量delta相乘this.matrix multiply(this.matrix, delta)最后把矩阵拼成matrix3d(...)写入transformtarget.style.transform matrix3d(${this.matrix.join(,)});这种用户持有矩阵状态、回调增量更新的模式正是 Warpable 支持 3D 变换README 中 Support 3d Transform的体现。5.6 Pinchable多指捏合开关属性为pinchable{true}。它的作用是在触控/多点输入场景下把拖拽、缩放、旋转、尺寸调整统一调度为捏合手势。官方注释说明了事件触发顺序onPinchStart发生在dragStart、rotateStart、scaleStart、resizeStart之前onPinch发生在drag、rotate、scale、resize之前onPinchEnd发生在dragEnd、rotateEnd、scaleEnd、resizeEnd之前。三个回调都提供target、clientX、clientY、datas跨事件阶段共享的自定义数据容器方便你在捏合开始阶段写入初始状态、在捏合过程中持续读取。5.7 keepRatio保持宽高比keepRatio{true}是一个全局性质的 Props官方注释为When resize or scale, keeps a ratio of the width, height即调整尺寸或缩放时保持目标的宽高比例适合头像裁剪、图片等比缩放等场景。六、Groupable多目标编组变换README 的能力列表中提到了 Groupable配套的 groupable.md 则给出了完整的编组用法指南。编组模式的核心变化有两个target从单个元素改为元素数组target{[].slice.call(document.querySelectorAll(.target))}事件从onDrag系列变为onDragGroup系列且回调参数中会多出targets组内所有目标与events组内每个目标各自对应的事件对象。groupable.md 中列出的编组事件全景如下draggableonDragGroupStart/onDragGroup/onDragGroupEndresizableonResizeGroupStart/onResizeGroup/onResizeGroupEndscalableonScaleGroupStart/onScaleGroup/onScaleGroupEndrotatableonRotateGroupStart/onRotateGroup/onRotateGroupEndpinchableonPinchGroupStart/onPinchGroup/onPinchGroupEnd。以拖拽编组为例onDragGroup中需要通过遍历events来逐个应用每个目标的变换onDragGroup{({ targets, events }) { events.forEach(ev { // ev 是组内单个目标的 drag 事件 console.log(onDrag left, top, ev.left, ev.top); // ev.target!.style.left ${ev.left}px; // ev.target!.style.top ${ev.top}px; console.log(onDrag translate, ev.dist); ev.target!.style.transform ev.transform; }); }}调整尺寸的编组处理最为精细onResizeGroup会拿到direction拖拽的方向向量同时每个ev内部还嵌套了一个ev.drag事件对象——它表示组整体 resize 时该成员发生的位移。groupable.md 给出的计算模式是onResizeGroup{({ targets, direction }) { e.events.forEach(ev { const offset [ direction[0] 0 ? -ev.delta[0] : 0, direction[1] 0 ? -ev.delta[1] : 0, ]; // ev.drag is a drag event that occurs when the group resize. const left offset[0] ev.drag.beforeDist[0]; const top offset[1] ev.drag.beforeDist[1]; const width ev.width; }); }}即当编组从左侧/上侧拉伸时成员元素需要向左/上产生补偿位移offset再叠加组内ev.drag.beforeDist才能保证从左边拉大组时所有成员仍然保持相对位置。同理缩放编组onScaleGroup与旋转编组onRotateGroup中每个ev同样携带ev.drag.beforeDist用于在 scale/rotate 的同时修正各成员的位置偏移。捏合编组的事件顺序与单个目标一致onPinchGroupStart/onPinchGroup/onPinchGroupEnd分别发生在对应的dragGroup、rotateGroup、scaleGroup、resizeGroup系列事件之前。编组能力的实际演示效果可参考仓库中的 groupable.gif七、本地开发与构建官方 README 的 Developments 一节说明了本地开发方式npm start以开发模式运行应用会打开demo/index.html文件。从 package.json 的脚本定义看start实际执行的是open ./demo/index.html rollup -c rollup.config.demo.js -w即先打开 demo 页面再以 watch 模式启动 rollup 构建修改源码后页面自动更新。此外package.json 中还定义了与发布相关的脚本可作参考build执行rollup -c产出dist目录下的构建产物随后npm run declaration生成类型声明最后用print-sizes ./dist打印产物体积declaration执行tsc -p tsconfig.declaration.json生成declaration/index.d.ts。需要说明的是start脚本中的open命令依赖本地环境支持且上述脚本面向该包自身的开发调试日常在自己的 Preact 工程中使用 preact-moveable 时只需按第三节的方式安装并引入即可。八、继续深入官方配套资料与许可证围绕 preact-moveable仓库内还提供了多份可直接查阅的配套资料Moveable HandbookMoveable 全系列的综合手册覆盖各能力的完整用法How to use Group编组用法的综合章节可与本包的 groupable.md 对照阅读How to use custom CSS自定义控制点样式的方法How to make custom able如何编写自定义能力Custom Able的指南本包的官方 demo 源码位于 App.tsx其中展示了ref获取组件实例、moveable.updateRect()手动刷新矩形、moveable.isMoveableElement()判断点击目标是否为 Moveable 控件等进阶用法。在许可证方面本包采用 MIT 许可见 LICENSE官方 README 亦注明 This project is MIT licensed允许自由使用、修改与分发但需保留版权声明Copyright (c) 2019 Daybrush与免责条款。小结preact-moveable 以一行Moveable组件的极简接入方式为 Preact 应用带来了完整的目标元素变换能力拖拽、调尺寸、缩放、旋转、扭曲、捏合、编组与吸附一应俱全且通过复用 react-moveable 引擎保证了与 React 生态完全一致的交互细节。使用时的关键心法可以概括为用开关 Props 开启能力、用节流 Props 控制步进、用 Start/进行中/End 三段事件同步目标样式当遇到多目标场景时切换到*Group系列事件并留意每个成员事件对象中嵌套的ev.drag位移数据。掌握这些要点后无论是做可视化编辑器、图片裁剪、拖拽布局还是 SVG 变形工具都能基于这套组件快速落地。赞分享前端UI组件【免费下载链接】moveableMoveable! Draggable! Resizable! Scalable! Rotatable! Warpable! Pinchable! Groupable! Snappable!项目地址https://gitcode.com/gh_mirrors/mo/moveable点击查看免费下载相关推荐ngx-moveable 完全指南在 Angular 中接入 Moveable 拖拽、缩放、旋转、扭曲与捏合能力ngx moveable 完全指南在 Angular 中接入 Moveable 拖拽、缩放、旋转、扭曲与捏合能力 ngx moveable 是 Moveabl前端UI组件lit-moveable 实战指南在 Lit 项目中集成 Moveable 的拖动、缩放、旋转、扭曲与吸附能力lit moveable 实战指南在 Lit 项目中集成 Moveable 的拖动、缩放、旋转、扭曲与吸附能力 lit moveable 是 Moveable前端UI组件React Moveable 完全指南在 React 中实现拖拽、缩放、旋转、捏合与吸附React Moveable 完全指南在 React 中实现拖拽、缩放、旋转、捏合与吸附 导读本文以 react moveable 为对象系统讲解如何在一前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询