返回

React 中使用拖拽让你轻松玩转界面交互

前端

前言

在 HTML5 标准出来之前,实现拖拽的大致思路是监听鼠标移动相关事件,来拖动目标元素到页面的任意位置,这要求目标元素必须满足绝对定位、脱离文档流才可以被移动,伪代码如下:

<div id="target" style="position: absolute;">
  <p>拖拽我</p>
</div>

<script>
  const target = document.getElementById('target');
  let isDragging = false;
  let offsetX = 0;
  let offsetY = 0;

  target.addEventListener('mousedown', (e) => {
    isDragging = true;
    offsetX = e.clientX - target.offsetLeft;
    offsetY = e.clientY - target.offsetTop;
  });

  document.addEventListener('mousemove', (e) => {
    if (!isDragging) {
      return;
    }

    target.style.left = e.clientX - offsetX + 'px';
    target.style.top = e.clientY - offsetY + 'px';
  });

  document.addEventListener('mouseup', () => {
    isDragging = false;
  });
</script>

这种方式虽然简单,但也有不少缺点:

  • 需要手动计算元素的偏移量,容易出错。
  • 无法实现元素之间的拖拽排序。
  • 不支持触屏设备。

为了解决这些问题,HTML5 标准引入了新的拖拽 API,使得拖拽操作变得更加简单和高效。

React 中的拖拽

React 中的拖拽功能主要通过 react-dnd 库来实现,它提供了丰富的 API 和组件,可以轻松实现各种拖拽操作。

1. 安装 react-dnd

npm install react-dnd --save

2. 创建可拖拽组件

首先,需要创建一个可拖拽组件,可以使用 react-dnd 提供的 Draggable 组件。

import { Draggable } from 'react-dnd';

const DraggableItem = (props) => {
  const { id, text, onDragStart, onDragEnd } = props;

  return (
    <Draggable
      id={id}
      type="ITEM"
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
    >
      <div>{text}</div>
    </Draggable>
  );
};

Draggable 组件中,需要指定 idtypeonDragStartonDragEnd 属性。

  • id 属性是元素的唯一标识。
  • type 属性是元素的类型,可以是任意字符串。
  • onDragStart 属性是拖拽开始时的回调函数。
  • onDragEnd 属性是拖拽结束时的回调函数。

3. 创建目标组件

接下来,需要创建一个目标组件,可以使用 react-dnd 提供的 Droppable 组件。

import { Droppable } from 'react-dnd';

const DroppableArea = (props) => {
  const { onDrop, children } = props;

  return (
    <Droppable
      accept="ITEM"
      onDrop={onDrop}
    >
      {children}
    </Droppable>
  );
};

Droppable 组件中,需要指定 acceptonDrop 属性。

  • accept 属性是目标组件可以接受的拖拽元素的类型。
  • onDrop 属性是拖拽元素被放置到目标组件时的回调函数。

4. 使用 react-dnd 的 Hooks

为了简化拖拽操作,react-dnd 提供了几个 Hooks,可以让我们更轻松地使用拖拽功能。

  • useDrag Hook:用于获取拖拽元素的 props。
  • useDrop Hook:用于获取目标组件的 props。
import { useDrag, useDrop } from 'react-dnd';

const DraggableItem = (props) => {
  const { id, text } = props;

  const [{ isDragging }, drag] = useDrag({
    id,
    type: 'ITEM',
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  return (
    <div ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
      {text}
    </div>
  );
};

const DroppableArea = (props) => {
  const { onDrop, children } = props;

  const [{ isOver, canDrop }, drop] = useDrop({
    accept: 'ITEM',
    drop: (item) => {
      onDrop(item);
    },
    collect: (monitor) => ({
      isOver: monitor.isOver(),
      canDrop: monitor.canDrop(),
    }),
  });

  return (
    <div ref={drop} style={{ border: isOver && canDrop ? '1px dashed blue' : '1px dashed gray' }}>
      {children}
    </div>
  );
};

5. 使用 react-dnd 的 API

除了 Hooks 之外,react-dnd 还提供了一些 API,可以让我们更灵活地控制拖拽操作。

  • DragSource:用于创建一个可拖拽元素。
  • DropTarget:用于创建一个目标组件。
import { DragSource, DropTarget } from 'react-dnd';

const DraggableItem = DragSource('ITEM', {
  beginDrag(props) {
    return { id: props.id };
  },
  endDrag(props, monitor) {
    if (monitor.didDrop()) {
      // 拖拽元素被放置到目标组件中
    }
  }
}, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging(),
}));

const DroppableArea = DropTarget('ITEM', {
  drop(props, monitor) {
    const item = monitor.getItem();
    // 拖拽元素被放置到目标组件中
  }
}, (connect, monitor) => ({
  connectDropTarget: connect.dropTarget(),
  isOver: monitor.isOver(),
  canDrop: monitor.canDrop(),
}));

结语

以上就是 React 中使用拖拽功能的详细介绍。通过 react-dnd 库,我们可以轻松实现各种拖拽操作,提升界面的交互性和用户体验。