Draggable DragSensor 指南:用原生 HTML5 Drag Drop API 驱动拖拽
Draggable DragSensor 指南用原生 HTML5 Drag Drop API 驱动拖拽【免费下载链接】draggableThe JavaScript Drag Drop library your grandparents warned you about.项目地址: https://gitcode.com/gh_mirrors/dr/draggable导读DragSensor 是 Shopify Draggable 拖拽库中一个特殊的传感器Sensor它不监听模拟的鼠标/触摸事件而是直接接管浏览器原生的 HTML5 Drag and Drop API把dragstart、dragover、dragend等原生拖拽事件翻译成 Draggable 统一的drag:start、drag:move、drag:stop传感器事件。本文以 DragSensor 官方文档 为主体结合 DragSensor 源码、基类 Sensor 与 单元测试带你掌握它的 API、配置项、工作原理、已知局限以及正确的接入方式。DragSensor 是什么接管原生浏览器拖拽在 Draggable 的传感器体系中MouseSensor 和 TouchSensor 通过监听mousedown/mousemove/touchstart等指针事件来模拟拖拽因此 Draggable 可以完全掌控整个拖拽过程并创建自己的视觉镜像Mirror。而DragSensor走的是另一条路线它捡起picks up浏览器原生的拖拽事件dragstart、dragover、dragend、drop、mousedown在containers指定的容器元素上触发以下 Draggable 传感器事件drag:startdrag:movedrag:stop这三个事件类型在 SensorEvent.ts 中定义为DragStartSensorEvent、DragMoveSensorEvent、DragStopSensorEvent均继承自SensorEvent携带clientX、clientY、target、container、originalSource、originalEvent等标准化属性。默认不启用文档开头有一句关键提示Draggable does not use this sensor by defaultDraggable 默认不使用此传感器。这在 Draggable.js 中有直接证据static Sensors {MouseSensor, TouchSensor};Draggable 静态挂载的默认传感器只有MouseSensor与TouchSensor。DragSensor虽然被 Sensors/index.js 导出但需要开发者显式传入sensors配置才会被激活。API构造、挂载与卸载new DragSensor(containers, options): DragSensor构造函数签名如下new DragSensor(containers: HTMLElement[]|NodeList|HTMLElement, options: Object): DragSensorcontainers传感器要监听的容器可以是HTMLElement数组、NodeList或单个HTMLElement。基类 Sensor 构造函数 会将其展开为数组[...containers]并把options浅拷贝到this.options。传感器事件始终在容器元素上触发trigger方法通过element.dispatchEvent派发见 Sensor.js#L111-L119外部监听时需在容器上绑定。dragSensor.attach(): void把传感器事件监听器挂到 DOM 上。对 DragSensor 而言attach()只做一件事attach() { document.addEventListener(mousedown, this[onMouseDown], true); }即在捕获阶段监听document上的mousedown作为整个拖拽流程的入口见 DragSensor.js#L66-L68。dragSensor.detach(): void与attach()对称移除mousedown捕获监听detach() { document.removeEventListener(mousedown, this[onMouseDown], true); }Options 配置项DragSensor 与 MouseSensor 共享基类Sensor的选项解析逻辑但文档只明确了两个配置项draggable {String}指定containers内可拖拽元素的 CSS 选择器。mousedown时源码用closest(target, this.options.draggable)向上查找命中元素const originalSource closest(target, this.options.draggable); if (!originalSource) { return; }只有当点击命中draggable选择器时传感器才会继续DragSensor.js#L210-L214。delay {Number}文档描述为延迟触摸开始delay touch start但从源码看它的真实语义更精确延迟把draggable属性写到源元素上从而延迟原生拖拽的启动。mousedown后源码会启动一个定时器this.mouseDownTimeout setTimeout(() { originalSource.draggable true; this.draggableElement originalSource; }, this.delay.drag);关键点是this.delay.drag。基类 Sensor.js#L1-L5 定义了默认延迟表const defaultDelay { mouse: 0, drag: 0, touch: 100, };calcDelaySensor.js#L127-L153支持三种传法不传三个键全部取默认值drag为0传数字三个键统一取该数值传对象如{drag: 200, touch: 100}缺省键回落默认值。因此若要长按才进入拖拽应配置delay: {drag: 200}这类对象形式。测试 DragSensor.test.js#L175-L178 用DRAG_DELAY验证了延迟后触发drag:start与延迟前松开鼠标不触发两个行为。注意由于draggable属性是延迟后才写上的在定时器触发前快速松开鼠标拖拽不会启动mouseup会调用reset()清除定时器与监听器见 DragSensor.js#L245-L272。测试does not trigger drag:start event releasing mouse before timeout正是对这一行为的回归验证DragSensor.test.js#L101-L124。源码中还支持的其它选项虽然文档只列出两个选项但从源码可以看到 DragSensor 还会用到基类继承下来的选项handle {String}指定拖拽手柄选择器。mousedown时若options.handle存在且点击目标不在手柄内直接返回DragSensor.js#L202-L208。type {String}拖拽允许的效果类型会被写入dataTransfer。dragstart时设置event.dataTransfer.effectAllowed this.options.typedragover时设置event.dataTransfer.dropEffect this.options.typeDragSensor.js#L85、#L140。distance无效见下文 Caveats。事件触发流程从 mousedown 到 drag:stop结合源码一次完整的 DragSensor 拖拽生命周期如下mousedown捕获[onMouseDown]先排除 Firefox 中表单控件/contenteditable的已知 bugbugzilla 739071 注释保留在 DragSensor.js#L190-L193随后依次校验容器命中、handle约束、draggable命中若命中的是原生可拖拽元素链接、图片等draggable属性为真的元素会先将其draggable置为false暂存再注册mouseup/dragstart/dragover/dragend/drop监听并按delay.drag启动定时器。dragstart[onDragStart]先写入dataTransfersetData(text, )注释标明Firefox 需要 text 键IE 需要该写法用document.elementFromPoint定位目标构造DragStartSensorEvent并通过setTimeout(..., 0)触发给drag:start的取消留出机会事件被cancel()时dragging置为false。dragover仅当this.dragging为真时触发DragMoveSensorEventdrag:move未取消则preventDefault()并设置dropEffect保证浏览器持续允许放置。dragend / dropdragend触发DragStopSensorEventdrag:stop随后调用reset()清理drop处理器仅preventDefault()阻止浏览器默认行为。reset清除定时器与全部临时监听并把nativeDraggableElement、draggableElement的draggable属性逐一还原保证 DOM 不被污染。Draggable 核心Draggable.js#L422-L495在捕获阶段监听drag:start等传感器事件再对外触发业务层的DragStartEvent/DragMoveEvent/DragStopEvent你可以在draggable.on(drag:start, ...)中做业务处理。在 Draggable 中使用 DragSensor文档给出的接入示例import {Draggable, Sensors} from shopify/draggable; const draggable new Draggable(containers, { sensors: [Sensors.DragSensor], }); // Remove default mouse sensor draggable.removeSensor(Sensors.MouseSensor);两点补充说明依据 Draggable.jssensors选项传入的传感器会与默认的MouseSensor、TouchSensor共存构造函数在 Draggable.js#L179-L184 合并defaultSensors与options.sensors。由于原生拖拽与鼠标模拟拖拽可能互相干扰文档示例紧接着用draggable.removeSensor(Sensors.MouseSensor)移除默认鼠标传感器removeSensor 实现实践中通常也应如此必要时同样移除TouchSensor。传感器实例由addSensor用new Sensor(this.containers, this.options)创建并自动attach()Draggable.js#L257-L266所以draggable:start监听已由框架接线业务层无需手动attach()。Known issues没有镜像Mirror原生拖拽 API 的代价写在文档的 Known issues 一节DragSensor 使用原生 Drag and Drop API因此 Draggable 不会为它创建镜像对镜像的控制也更少。原因很直接浏览器在dragstart后会自动绘制一张半透明快照跟随光标系统级拖拽图像这张图像不受 JavaScript 完全控制Draggable 的 Mirror 插件 所实现的克隆元素、动画、样式跟随等能力在这一传感器下无法完整生效。如果你的交互依赖镜像效果例如排序动画、占位样式应优先考虑默认的 MouseSensor/TouchSensor。Caveatsdistance 选项无效文档末尾列出关键限制distance选项在 DragSensor 下不生效。distance的本意是指针移动超过指定距离后才开始拖拽见 MouseSensor 文档 的说明。但原生拖拽的启动由浏览器在dragstart时决定DragSensor 无法在按下—移动—达到阈值—才启动之间插入控制逻辑因此该选项被忽略。draggable命中判断、delay.drag延迟与handle约束是它在启动阶段仅有的可调手段。测试覆盖行为即规范DragSensor.test.js 用沙箱 DOM 与模拟事件流验证了核心契约可直接作为使用参考拖拽流程按下 → 延迟 → dragstart → dragover → dragend → 松开依次触发drag:start、drag:move、drag:stopL71-L152监听drag:start并调用event.detail.cancel()可以取消整次拖拽L84-L99点击非draggable命中的元素不触发drag:startL154-L172设置delay后延迟前松开鼠标不会触发任何拖拽事件L195-L219。总结与选型建议DragSensor 适合原生拖拽体验可接受、且希望省去自绘镜像成本的场景例如简单的列表重排、文件拖拽入口等它的取舍非常清晰优势直接复用浏览器成熟的拖放实现代码量小dataTransfer可携带数据为跨容器甚至跨页面放置留了可能代价无 Draggable 镜像、无distance阈值、对拖拽过程的自定义能力弱于基于指针事件模拟的默认传感器。若要精细控制拖拽视觉与交互镜像、排序动画、吸附等请使用默认的 MouseSensor/TouchSensor若仅需能拖就行或需要与原生放置目标协作再把 DragSensor 通过sensors选项接入并按文档示例移除冲突的默认传感器。【免费下载链接】draggableThe JavaScript Drag Drop library your grandparents warned you about.项目地址: https://gitcode.com/gh_mirrors/dr/draggable创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考