「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
背景:前几日,有小伙伴问我如何实现物体的拖拽,当时的回答即 onMouseDown、onMouseMove 和 onMouseUp 事件结合起来做。小伙伴当时觉得这么实现非常的麻烦,于是我又提供了另外一种思路,即通过 drag 和 drop 来实现此效果。
drag、drop 是什么
drag 和 drop 是一个 API,它可以让页面中的绝大部分元素都变得可拖拽,比如,用户可以通过鼠标选择可拖拽元素,将元素拖拽到可放置元素上,并释放鼠标以便放置这些元素。拖拽期间,会有一个拖拽元素的半透明快照跟随鼠标指针。默认情况下,只有图片、链接、选择的文本可以被拖拽。如果想要其他元素可拖拽,则需要在元素上设置 draggable=true。
<div draggable="true">这是一个可拖拽的元素</div>
当拖拽事件发生时,被拖拽的元素会依次触发 onDragStart、onDrag 和 onDragEnd,可放置元素会触发 onDragEnter、onDragOver 和 onDrop 事件,当被拖拽的元素离开可放置元素上方,还会触发 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 个字段:name、size 和 type。
当发生拖动时,浏览器会自动生成一个半透明的图像,并在拖拽过程中跟随鼠标。
DataTransfer 还支持哪些场景
上文所述的大部分拖拽场景都是在同一个浏览器,如果当前需求是把某一个元素从 A 浏览器拖拽到 B 浏览器某一位置,那么我们应该怎么实现呢?乍一看,这样的需求貌似是无法实现的,但是我们使用dataTransfer就可以做到。如果你想做跨浏览器的拖拽,唯一需要注意的是拖拽数据的类型,现在大部分的应用程序均支持 text/html、text/plain 和 text/uri-list。当然,你也可以使用自定义拖拽数据的类型。
text/html:在contentEditable的元素中渲染数据。text/plain:作为代码编辑器的内容、input元素标签的value。text/uri-list:放置在浏览器中,会导航到URL;如果放置于桌面,则会创建快捷方式。
总结
本文主要讲解 drag 和 drop 的应用,也介绍可以利用这两个 API 可以实现跨浏览器的拖拽。当然,如果你对上述跨端的拖拽场景感兴趣的话,也可以尝试下 Transmat 库,这个库主要简化了在 Web 应用程序中传输和接收数据的过程。