前言

https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API https://juejin.cn/post/7075918201359433758#heading-16

html5 中提供了一系列Drag and Drop 接口,主要包括四部分:DragEventDataTansferDataTransferItemDataTransferItemList

API

DragEvent

两种元素

  • 被拖拽元素:被拖的东西
  • 目标元素:放的地方

    事件

    | 事件 | 事件处理程序 | 事件主体 | 触发时机 | | —- | —- | —- | —- | | dragstart | ondragstart | 源元素 | 当源元素开始被拖拽。 | | drag | ondrag | 源元素 | 当源元素被拖拽(持续触发)。 | | dragend | ondragend | 源元素 | 当源元素拖拽结束(鼠标释放或按下esc键) | | dragenter | ondragenter | 目标元素 | 当被拖拽元素进入该元素。 | | dragover | ondragover | 目标元素 | 当被拖拽元素停留在该元素(持续触发)。 | | dragleave | ondragleave | 目标元素 | 当被拖拽元素离开该元素。 | | drop | ondrop | 目标元素 | 当拖拽事件在合法的目标元素上释放。 |

事件顺序

被拖拽元素:dragstart->drag->dragend
目标元素:dragenter->dragover->drop/dropleave

tips

如果某个元素同时设置了dragoverdrop的监听,那么必须阻止dragover默认行为,否则drop不会被触发

DataTransfer

就是拖放过程中对数据进行传输
其中setData用来存放数据,getData用来获取数据,出于安全的考量,数据只能在drop时获取,而effectAlloweddropEffect则影响鼠标展示的样式

其他两个就不说了,看mdn

实操

改变组件位置

  • position :abs通过top/left等直接改变元素的位置
  • css的transform属性中的translate对元素的位置进行改变

第二种好,基于自身位移、且通过gpu计算

拖拽向量

DragEvent继承自MouseEvent,所以我们可以通过MouseEvent接口的offsetX属性和offsetY属性获取鼠标现在相对于该物体的位置差

实现1.0

获取初始位置

Window.getComputedStyle()方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有 CSS 属性的值。 私有的 CSS 属性值可以通过对象提供的 API 或通过简单地使用 CSS 属性名称进行索引来访问。 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getComputedStyle

然后根据x、y轴上向量修改 transform 属性

  1. sourceElem.addEventListener('dragend', (e) => {
  2. const startPosition = window.getComputedStyle(e.target).transform;
  3. e.target.style.transform = `${startPosition} translate(${e.offsetX}px, ${e.offsetY}px)`;
  4. }, true);

实现1.1

实际上,还需要靠考虑鼠标在元素的位置

  1. function enableDrag(element) {
  2. let mouseDiff = null;
  3. element.addEventListener('dragstart', (e) => {
  4. //初始时鼠标与元素的位置差
  5. mouseDiff = `translate(${-e.offsetX}px, ${-e.offsetY}px)`
  6. }, true);
  7. element.addEventListener('dragend', (e) => {
  8. //开始时元素的位置
  9. const startPosition = window.getComputedStyle(e.target).transform;
  10. //鼠标移动的位置
  11. const mouseMove = `translate(${e.offsetX}px, ${e.offsetY}px)`;
  12. e.target.style.transform = `${mouseDiff} ${startPosition} ${mouseMove}`;
  13. }, true);
  14. }
  15. enableDrag(souceElement);

图的连线

节点用DOM,连线用SVG
一个图由nodesedges两部分组成 ,数组存储, 每个node一个唯一的id,使用Map去映射id与对应的positon形如[x,y]的关系,而edge有源端与终端的id,通过id去获得对应的坐标