drag 和 drop实现拖拽效果

2,727 阅读3分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

背景:前几日,有小伙伴问我如何实现物体的拖拽,当时的回答即 onMouseDown、onMouseMove 和 onMouseUp 事件结合起来做。小伙伴当时觉得这么实现非常的麻烦,于是我又提供了另外一种思路,即通过 drag 和 drop 来实现此效果。

drag、drop 是什么

dragdrop 是一个 API,它可以让页面中的绝大部分元素都变得可拖拽,比如,用户可以通过鼠标选择可拖拽元素,将元素拖拽到可放置元素上,并释放鼠标以便放置这些元素。拖拽期间,会有一个拖拽元素的半透明快照跟随鼠标指针。默认情况下,只有图片、链接、选择的文本可以被拖拽。如果想要其他元素可拖拽,则需要在元素上设置 draggable=true

<div draggable="true">这是一个可拖拽的元素</div>

当拖拽事件发生时,被拖拽的元素会依次触发 onDragStartonDragonDragEnd,可放置元素会触发 onDragEnteronDragOveronDrop 事件,当被拖拽的元素离开可放置元素上方,还会触发 onDragLeave 事件。 如果想把元素放置到可放置元素上,需做如下设置。如果不设置,则可放置元素的 onDrop 事件无法触发。

const onDragEnter = useCallback((e: React.DragEvent) => {
	e.preventDefault();
}, []);
const onDragOver = useCallback((e: React.DragEvent) => {
	e.preventDefault();
}, []);

到此,我们已经可以拖动元素,也可以让元素接收到 onDrop 事件。

传递数据

每一个拖拽事件都有 dataTransfer 对象,可以通过对象上的 setData 方法存储拖拽数据。拖拽数据主要包含两个信息,数据的类型和数据值。一般都会在 onDragStart 事件中存储拖拽数据,在 onDrop 事件中读取数据。使用方法如下:

const onDragStart = useCallback((e: React.DragEvent, item) => {
	e.dataTransfer.setData('text/plain', '这是一个可拖拽的元素');
}, []);
const onDrop = useCallback((e: React.DragEvent, item) => {
	const data = e.dataTransfer.getData('text/plain');
	...
}, []);

dataTransfer 对象上 files 属性通常存储从桌面往浏览器中拖拽的文件列表,目前的应用场景大多为拖拽上传。每一个文件上都包含 3 个字段:namesizetype。 当发生拖动时,浏览器会自动生成一个半透明的图像,并在拖拽过程中跟随鼠标。

DataTransfer 还支持哪些场景

上文所述的大部分拖拽场景都是在同一个浏览器,如果当前需求是把某一个元素从 A 浏览器拖拽到 B 浏览器某一位置,那么我们应该怎么实现呢?乍一看,这样的需求貌似是无法实现的,但是我们使用dataTransfer就可以做到。如果你想做跨浏览器的拖拽,唯一需要注意的是拖拽数据的类型,现在大部分的应用程序均支持 text/htmltext/plaintext/uri-list。当然,你也可以使用自定义拖拽数据的类型。

  • text/html:在 contentEditable 的元素中渲染数据。
  • text/plain:作为代码编辑器的内容、input 元素标签的 value
  • text/uri-list:放置在浏览器中,会导航到 URL;如果放置于桌面,则会创建快捷方式。

总结

本文主要讲解 dragdrop 的应用,也介绍可以利用这两个 API 可以实现跨浏览器的拖拽。当然,如果你对上述跨端的拖拽场景感兴趣的话,也可以尝试下 Transmat 库,这个库主要简化了在 Web 应用程序中传输和接收数据的过程。

参考文献