react-dnd API拖拽工具详细用法示例

目录
  • 前言
  • 概念
  • 核心API
    • DndProvider
    • Backend
    • useDrag
      • useDrag返回三个参数
      • useDrag传入两个参数
      • DragSourceMonitor对象
    • useDrop
      • useDrag返回两个参数
      • useDrop传入一个参数
      • DropTargetMonitor对象
    • 数据流转
    • 拖拽预览
      • DragPreviewImage
      • useDragLayer
  • 其他使用场景
    • 批量拖拽
    • 拖拽排序
  • 最后

前言

最近公司准备开发一个审批流系统,其中会用到拖拽工具来搭建流程,关于拖拽的实现我们选择了react-dnd这个库,本文总结了react-dnd API的详细用法,并附上不同场景的demo,希望对大家有用。

概念

React DnD 是一组 React 高阶组件,使用的时候只需要使用对应的 API 将目标组件进行包裹,即可实现拖动或接受拖动元素的功能。

在拖动的过程中,不需要开发者自己判断拖动状态,只需要在传入的 spec 对象中各个状态属性中做对应处理即可,因为react-dnd使用了redux管理自身内部的状态。

Some of these concepts resemble the Flux and Redux architectures.
This is not a coincidence, as React DnD uses Redux internally.

值得注意的是,react-dnd并不会改变页面的视图,它只会改变页面元素的数据流向,因此它所提供的拖拽效果并不是很炫酷的,我们可能需要写额外的视图层来完成想要的效果,但是这种拖拽管理方式非常的通用,可以在任何场景下使用,非常适合用来定制。

核心API

介绍实现拖拽和数据流转的核心API,这里以Hook为例。

DndProvider

如果想要使用 React DnD,首先需要在外层元素上加一个 DndProvider。

import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
<DndProvider backend={HTML5Backend}>
    <TutorialApp />
</DndProvider>

DndProvider 的本质是一个由 React.createContext 创建一个上下文的容器(组件),用于控制拖拽的行为,数据的共享,类似于react-redux的Provider。

Backend

上面我们给DndProvider传的参数是一个backend,那么这里来解释一下什么是backend

React DnD 将 DOM 事件相关的代码独立出来,将拖拽事件转换为 React DnD 内部的 redux action。由于拖拽发生在 H5 的时候是 ondrag,发生在移动设备的时候是由 touch 模拟,React DnD 将这部分单独抽出来,方便后续的扩展,这部分就叫做 Backend。它是 DnD 在 Dom 层的实现。

  • react-dnd-html5-backend : 用于控制html5事件的backend
  • react-dnd-touch-backend : 用于控制移动端touch事件的backend
  • react-dnd-test-backend : 用户可以参考自定义backend

useDrag

让DOM实现拖拽能力的构子,官方用例如下

import { DragPreviewImage, useDrag } from 'react-dnd';
export const Knight: FC = () => {
    const [{ isDragging }, drag, preview] = useDrag(
        () => ({
            type: ItemTypes.KNIGHT,
            collect: (monitor) => ({
                isDragging: !!monitor.isDragging()
            })
        }),
        []
    );
    return (
        <>
            <DragPreviewImage connect={preview} src={knightImage} />
            <div
                ref={drag}
            >
                ♘
            </div>
        </>
    );
};

useDrag返回三个参数

第一个返回值是一个对象 表示关联在拖拽过程中的变量,需要在传入useDrag的规范方法的collect属性中进行映射绑定,比如:isDraging,canDrag等

第二个返回值 代表拖拽元素的ref

第三个返回值 代表拖拽元素拖拽后实际操作到的dom

useDrag传入两个参数

  • 第一个参数,是一个对象,是用于描述了drag的配置信息,常用属性

type: 指定元素的类型,只有类型相同的元素才能进行drop操作

item: 元素在拖拽过程中,描述该对象的数据,如果指定的是一个方法,则方法会在开始拖拽时调用,并且需要返回一个对象来描述该元素。

end(item, monitor): 拖拽结束的回调函数,item表示拖拽物的描述数据,monitor表示一个 DragTargetMonitor 实例

isDragging(monitor):判断元素是否在拖拽过程中,可以覆盖Monitor对象中的isDragging方法,monitor表示一个 DragTargetMonitor 实例

isDragging: (monitor) => {
  return monitor.getItem() ? index === monitor.getItem().index : false;
},
collect: (monitor: any) => ({
    //当传入isDragging方法时,monitor.isDragging()方法指代传入的方法
  isDragging: monitor.isDragging(),
}),

canDrag(monitor):判断是否可以拖拽的方法,需要返回一个bool值,可以覆盖Monitor对象中的canDrag方法,与isDragging同理,monitor表示一个 DragTargetMonitor 实例

collect:它应该返回一个描述状态的普通对象,然后返回以注入到组件中。它接收两个参数,一个 DragTargetMonitor 实例和拖拽元素描述信息item

  • 第二个参数是一个数组,表示对方法更新的约束,只有当数组中的参数发生改变,才会重新生成方法,基于react的useMemo实现

DragSourceMonitor对象

DragSourceMonitor是传递给拖动源的收集函数的对象。它的方法允许您获取有关特定拖动源的拖动状态的信息。 常用的方法: canDrag():描述元素是否可以拖拽,返回一个bool值

isDragging():判断元素是否在拖拽过程中,返回一个bool值

getItemType():获取元素的类型,返回一个bool值

getItem():获取元素的描述数据,返回一个对象

getDropResult():拖拽结束,返回拖拽结果的构子,可以拿到从drop元素中返回的数据

didDrop(): 拖拽结束,元素是否放置成功,返回一个bool值

getDifferenceFromInitialOffset(): 获取相对于拖拽起始位置的相对偏移坐标。

useDrop

实现拖拽物放置的钩子,官方用例如下

function BoardSquare({ x, y, children }) {
  const black = (x + y) % 2 === 1
  const [{ isOver }, drop] = useDrop(() => ({
    accept: ItemTypes.KNIGHT,
    drop: () => moveKnight(x, y),
    collect: monitor => ({
      isOver: !!monitor.isOver(),
    }),
  }), [x, y])
  return (
    <div
      ref={drop}
      style={{
        position: 'relative',
        width: '100%',
        height: '100%',
      }}
    >
     ....
    </div>,
  )
}
export default BoardSquare

useDrag返回两个参数

  • 第一个返回值是一个对象,表示关联在拖拽过程中的变量,需要在传入useDrop的规范方法的collect属性中进行映射绑定
  • 第二个返回值代表放置元素的ref

useDrop传入一个参数

用于描述drop的配置信息,常用属性

accept: 指定接收元素的类型,只有类型相同的元素才能进行drop操作

drop(item, monitor): 有拖拽物放置到元素上触发的回调方法,item表示拖拽物的描述数据,monitor表示 DropTargetMonitor实例,该方法返回一个对象,对象的数据可以由拖拽物的monitor.getDropResult方法获得

hover(item, monitor):当拖住物在上方hover时触发,item表示拖拽物的描述数据,monitor表示 DropTargetMonitor实例,返回一个bool值

canDrop(item, monitor):判断拖拽物是否可以放置,item表示拖拽物的描述数据,monitor表示 DropTargetMonitor实例,返回一个bool值

DropTargetMonitor对象

DropTargetMonitor是传递给拖放目标的收集函数的对象。它的方法允许您获取有关特定拖放目标的拖动状态的信息。 常用的方法:

canDrop():判断拖拽物是否可以放置,返回一个bool值

isOver(options): 拖拽物掠过元素触发的回调方法,options表示拖拽物的options信息

getItemType():获取元素的类型,返回一个bool值

getItem():获取元素的描述数据,返回一个对象

didDrop(): 拖拽结束,元素是否放置成功,返回一个bool值

getDifferenceFromInitialOffset(): 获取相对于拖拽起始位置的相对偏移坐标。

数据流转

看了API之后,实际上不能很好的认识到每个状态和每个方法的工作流程,所以,我这里画了一张图,帮助你更清晰的看到它的数据是如何流动的。

然后我们通过一个demo来更深刻的认识这个过程

这里我们定义了几个单词,然后通过拖拽,将它放入对应的分组里面

单词代码

const Word: FC = ({ type, text, id, ...props }: any) => {
  const [offsetX, setOffsetX] = useState(0);
  const [offsetY, setOffsetY] = useState(0);
  const [{ isDragging }, drag]: any = useDrag(() => ({
    type,
    item: { id, type },
    end(item, monitor) {
      let top = 0,
        left = 0;
      if (monitor.didDrop()) {
        const dropRes = monitor.getDropResult() as any;
        //获取拖拽对象所处容器的数据,获取坐标变化
        if (dropRes) {
          top = dropRes.top;
          left = dropRes.left;
        }
        //这里必须写成函数的传入方式,否则无法获取上一个state
        setOffsetX((offsetX) => offsetX + left);
        setOffsetY((offsetY) => offsetY + top);
      } else {
        // 移出则回到原位
        setOffsetX(0);
        setOffsetY(0);
      }
    },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  }));
  return ...   // dom
  );
};

分组代码

function Classification({ type, title }: any) {
  const [{ isOver, canDrop }, drop] = useDrop(
    () => ({
      accept: type,
      drop(_item: any, monitor: any) {
         // 获取每一次放置相对于上一次的偏移量
        const delta = monitor.getDifferenceFromInitialOffset();
        const left = Math.round(delta.x);
        const top = Math.round(delta.y);
        // 回传给drag
        return { top, left };
      },
      canDrop: (_item, monitor) => {
        const item = monitor.getItem() as any;
        return item.type === type;
      },
      collect: (monitor) => ({
        isOver: !!monitor.isOver(),
        canDrop: !!monitor.canDrop(),
      }),
    }),
    [],
  );
  return ...   // dom
  )

完整demo戳链接:github.com/AdolescentJ…

拖拽预览

受限于浏览器API的控制,拖拽元素在开始拖拽之后,只能保持其本身的一个样式,不能使用其他样式预览,针对此,react-dnd为我们提供了两种解决方法。

DragPreviewImage

react-dnd提供的DragPreviewImage组件,让我们在拖拽的时候,可以以图片的形式预览拖拽的元素

它接收两个参数,一个是useDrag返回的预览ref,一个是需要预览的图片

使用

import { useDrag, DragPreviewImage } from 'react-dnd';
import apple from '../../assets/apple.png';
const DragPreviewImg = () => {
  const [{ isDragging }, drag, preview] = useDrag({
    type: 'DragDropBox',
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });
  return (
    <>
      <DragPreviewImage connect={preview} src={apple} />
      <div
        className='card_drag'
        ref={drag}
        style={{
          opacity: isDragging ? 0.5 : 1,
        }}
      >
        drag item prview an apple
      </div>
    </>
  );
};
export default DragPreviewImg;

效果

完整demo戳链接:github.com/AdolescentJ…

使用图片来预览拖拽元素确实可以解决一部分问题,但在实际场景中,拖拽的元素可能会很多,我们也不能找UI把所有类型的图都给一张,并且很多比较复杂的dom图片是不能取代的

所以要展示更加定制化的预览样式,我们可以使用下面这种。

useDragLayer

useDragLayer是一个钩子,它允许你使用dom的方式自定义拖拽元

它的原理是,监听你的拖拽状态,在拖拽的时候,可以取到你拖拽状态的数据,并且它会创建一个你想预览的dom,然后我们可以可以通过拖拽的状态来改变它的样式,达到取代预览的效果。

使用

const CustomDragLayer = (props: any) => {
//monitor 是drag的 monitor
    const { itemType, isDragging, item, initialOffset, currentOffset } = useDragLayer((monitor) => ({
        item: monitor.getItem(),
        itemType: monitor.getItemType(),
        initialOffset: monitor.getInitialSourceClientOffset(),
        currentOffset: monitor.getSourceClientOffset(),
        isDragging: monitor.isDragging(),
      }));
      if (!isDragging) {
        return null;
      }
      return (
        <div style={layerStyles}>
            <div className='card_drag'>这里是预览样式</div>
        </div>
      );
}

预览

完整demo戳链接:github.com/AdolescentJ…

其他使用场景

除了上面的例子,还有非常多的案例

批量拖拽

可以选择多个元素进行拖拽

拖拽排序

可以拖拽元素放置排序

完整demo戳链接:github.com/AdolescentJ…

最后

感谢你能看到这里,本文总结了react-dnd的API的使用以及常见的场景,希望对你有所帮助,后续会一直更新,当然,如果可以的话不妨留一个赞再走呢。

参考链接

react-dnd.github.io/react-dnd/d…

https://www.jb51.net/article/265220.htm

以上就是react-dnd API拖拽工具详细用法示例的详细内容,更多关于react-dnd API拖拽工具的资料请关注我们其它相关文章!

(0)

相关推荐

  • react+react-beautiful-dnd实现代办事项思路详解

    目录 react+react-beautiful-dnd应用 效果预览 实现思路 index.js入口文件配置 app.jsx主页面配置 untils/with-context.js封装工具todoContext components/TodoHeader.jsx页面头部 components/TodoInput.jsx该文件主要负责添加事件 格式DragDropContext最外面盒子Droppable第二层盒子 格式Draggable最里面盒子 components/TodoList.jsx

  • react-beautiful-dnd 实现组件拖拽

    一个React.js 的 漂亮,可移植性 列表拖拽库.想了解更多react-beautiful-dnd特点适用人群请看官方文档.中文翻译文档 npm:https://www.npmjs.com/package/react-beautiful-dnd 1.安装 ​ 在已有react项目中 执行以下命令 so easy. # yarn yarn add react-beautiful-dnd # npm npm install react-beautiful-dnd --save 2.APi 详情查

  • 使用react-beautiful-dnd实现列表间拖拽踩坑

    为什么选用react-beautiful-dnd 相比于react-dnd,react-beautiful-dnd更适用于列表之间拖拽的场景,支持移动端,且较为容易上手. 基本使用方法 基本概念 DragDropContext:构建一个可以拖拽的范围 onDragStart:拖拽开始回调 onDragUpdate:拖拽中的回调 onDragEnd:拖拽结束时的回调 Droppable - 可以放置拖拽块的区域 Draggalbe - 可被拖拽的元素 使用方法 把你想能够拖放的代码放到DragDr

  • React DnD如何处理拖拽详解

    目录 正文 代码结构 DndProvider DragDropManager useDrag HTML5Backend TouchBackend 总结 正文 React DnD 是一个专注于数据变更的 React 拖拽库,通俗的将,你拖拽改变的不是页面视图,而是数据.React DnD 不提供炫酷的拖动体验,而是通过帮助我们管理拖拽中的数据变化,再由我们根据这些数据进行渲染.我们可能需要写额外的视图层来完成想要的效果,但是这种拖拽管理方式非常的通用,可以在任何场景下使用.初次使用可能感觉并不是那

  • react-dnd实现任意拖动与互换位置

    本文实例为大家分享了react-dnd实现任意拖动与互换位置的具体代码,供大家参考,具体内容如下 react-dnd用法 hooks组件 1.使用DndProvider定义一个可以拖拽的范围 import { HTML5Backend } from 'react-dnd-html5-backend'; import { DndProvider } from 'react-dnd'; class App extends Component{   render () {     return (  

  • react-dnd API拖拽工具详细用法示例

    目录 前言 概念 核心API DndProvider Backend useDrag useDrag返回三个参数 useDrag传入两个参数 DragSourceMonitor对象 useDrop useDrag返回两个参数 useDrop传入一个参数 DropTargetMonitor对象 数据流转 拖拽预览 DragPreviewImage useDragLayer 其他使用场景 批量拖拽 拖拽排序 最后 前言 最近公司准备开发一个审批流系统,其中会用到拖拽工具来搭建流程,关于拖拽的实现我们

  • react实现自定义拖拽hook

    前沿 最近发现公司的产品好几个模块用到了拖拽功能,之前拖拽组件是通过Html5 drag Api 实现的但体验并不是很好,顺便将原来的拖拽组建稍做修改,写一个自定义hook,方便大家使用拖拽功能. 正文 拖拽功能原理: 1.拖拽元素通过addEventListener监听器添加鼠标按下,鼠标移动,以及鼠标抬起事件.2.再通过getBoundingClientRect() 得到拖拽元素四周相对于可拖拽区域边界的距离.3.鼠标移动时计算x轴和y轴的移动偏移量.4.通过element.style.tr

  • React实现卡片拖拽效果流程详解

    前提摘要: 学习宋一玮 React 新版本 + 函数组件 &Hooks 优先 开篇就是函数组件+Hooks 实现的效果如下: 学到第11篇了 照葫芦画瓢,不过老师在讲解的过程中没有考虑拖拽目标项边界问题,我稍微处理了下这样就实现拖拽流畅了 下面就是主要的代码了,实现拖拽(src/App.js): 核心在于标记当前项,来源项,目标项,并且在拖拽完成时对数据处理,更新每一组数据(useState): /** @jsxImportSource @emotion/react */ // 上面代码是使用e

  • Fabric.js 拖拽平移画布方法示例

    目录 正文 原理解析 按下鼠标时 移动鼠标时 松开鼠标时 代码仓库 正文 使用 fabric.js 创建出来的画布默认是不能拖拽移动的. 不过我们可以利用一些小技巧让画布具有被拖拽的能力,fabric.js 官网也提供了一个 demo ,但文档上并没有详细的讲解拖拽画布的实现原理. 本文就粗略分析一下这个原理. 原理解析 鼠标拖拽的原理其实很简单,主要就3步: 鼠标点击元素 移动鼠标 松开鼠标 在鼠标移动时,获取鼠标当前位置,然后修改被拖拽元素的位置. 当松开鼠标时,也要获取松手那刻鼠标所在位置

  • Android 实现仿QQ拖拽气泡效果的示例

    目录 效果图: 一.实现思路 二.功能实现 三.全屏拖拽效果实现 源码地址: 效果图: 一.实现思路 在列表中默认使用自定义的TextView控件来展示消息气泡,在自定义的TextView控件中重写onTouchEvent方法,然后在DOWN.MOVE.UP事件中分别处理拖拽效果. 整个拖拽效果我们可以拆分成以下几步来实现: 1.默认状态 2.两气泡相连状态 3.两气泡分离状态 4.气泡消失状态 二.功能实现 默认状态:用来做一个状态的标识,无需特别处理. 两气泡相连状态:绘制一个固定圆和一个移

  • 微信小程序拖拽排序列表的示例代码

    拖拽排序列表 思路 界面分为两层: 底层,正常列表展示,拖拽的时候不做处理(大牛直接加了动画,原谅我技艺不精,还没实现) 顶层,movable-view组件,不长按不展示,之后长按才展示,且没有点击事件. 事件 主要监听:longpress , touchmove , touchend 三个事件 longpress 保障长按才有效,并设定许多其他值. touchmove 滑动的时候触发 判断是否需要滑动页面,因为 movable-area组件 滑动事件被catch掉,无法滑动: 记录滑动经过的项

  • 基于Cesium实现拖拽3D模型的示例代码

    目录 添加基站模型 拖拽 这个地方是想实现一个什么效果呢?就是使用 cesium 在地图上添加一个3D模型,然后实现拖拽效果. 添加基站模型 然后这篇博文介绍的主要不是添加模型,但是也简单把代码直接粘贴过来吧,就不详细说了. // 添加基站模型 function addSite() { let position = Cesium.Cartesian3.fromDegrees(116.236393, 40.075119, 0); // 设置模型方向 let hpRoll = new Cesium.

  • WinForm实现的图片拖拽与缩放功能示例

    本文实例讲述了WinForm实现的图片拖拽与缩放功能.分享给大家供大家参考,具体如下: 最近做项目的时候遇到上传施工平面布置图,查看,因为图片比较大,一般的显示器分辨率无法显示全,然后还需要放大看清楚图片里面的文字内容,所以需要用到图片的拖拽与缩放功能.这里整理下具体操作. 首先新建一个窗体,拖一个panel控件到窗体中,然后在拖一个pictureobx控件到panel中,然后在添加个上传图片的按钮: 具体代码: using System; using System.Collections.Ge

  • JS实现的文件拖拽上传功能示例

    本文实例讲述了JS实现的文件拖拽上传功能.分享给大家供大家参考,具体如下: <!doctype html> <html> <head> <meta charset="utf-8"> <title>www.jb51.net JS文件拖拽上传</title> <style> div{ width: 300px; height: 300px; border:1px dashed #000; position:

随机推荐