一些与拖拽相关的技术点

采用 HTML5 dnd 拖拽事件是容易的,但有很多限制。


拖拽事件的选择

  1. HTML5 Drag and Drop API
  2. 利用 Mouse 事件来实现 Drag And Drop

采用 HTML5 dnd 拖拽事件是容易的,但有很多限制,例如触摸屏的支持、仅 XY 轴拖拽的交互处理、跨窗口的拖拽,而利用 Mouse 事件我们能够做的上限更高。

利用 Mouse 事件做 dnd 的一般实现:

  1. Draggable,在 mousedown 将元素设置为 position: absolute, 隐藏当前节点并生成一个 ghost 节点来辅助, 而在 mousemove 的时候将 ghost 节点的位置更新,可以使用 position 的位置计算也可以使用 transform 来变换位置。
  2. Droppable,标记可以放置元素的容器节点,在 mousemove 的时候根据当前的位置需要探测可以 droppable 的节点,可以采取 document.elementFromPoint(x, y) API 来找到当前位置的节点,而当前位置最上层的节点是 draggable 的节点,所以我们需要将 draggable 中的节点先 hidden 然后在探测 droppable 相关的元素再恢复 visible。
  3. Snappable, mousemove 的时候,计算容器内所有需被计算的节点(可以外部传入,默认为容器内所有可以拖拽的元素)的 offset, 然后根据节点的位置进行辅助线的渲染(分为水平和垂直的两个方向)

代码详情可以看 利用 mouse 实现 dnd 例子

Vue Component DSL

以 Vue-smooth-dnd 的组件为例, 分为 Container 组件, Container 组件包含 Draggable 组件,而 Draggable 包含的即是需要被拖拽的 DOM 元素。

<!-- 单个容器 -->
  <Container
    orientation="horizontal"
    @drag-start="e => log('drag start', e)"
    @drag-end="e => log('drag end', e)"
  >
    <Draggable v-for="card in cards" :key="card.id">
      <div :class="card.props.className" :style="card.props.style">
        <p>{{ card.data }}</p>
      </div>
    </Draggable>
  </Container>

<!-- 容器嵌套 -->
<Container>
    <Draggable v-for="parent in parents" :key="parent.id">
        <div class="parent">
            <p>{{ parent.name }}</p>
            <Container>
                <Draggable v-for="child in parent.children" :key="child.id">
                    <div class="child">
                        <p>{{ child.name }}</p>
                    </div>
                </Draggable>
            </Container>
        </div>
    </Draggable>
</Container>

拖拽相关的库

  1. Jquery-ui
  2. Moveable.js
  3. Vue-smooth-dnd
  4. React-dnd
  5. Dnd-kit