简介

最近在研究用 React 绘制拓扑图的时候涉及到了 HTML5 拖放 API,了解到了 React DnD 这个拖放神器。React DnD 帮我们封装了一系列的拖放 API,大大简化了拖放 API 的使用方式,今天就结合下面这个示例给大家介绍下 React DnD 的用法。

重要概念

React Dnd 提供了几个重要的 API 供我们使用:

DragSource

DropTarget

DragDropContext && DragDropContextProvider

DragSource

DragSource 是一个高阶组件,使用 DragSource 高阶组件包裹的组件可以进行拖拽操作。

基本用法:

import { DragSource } from 'react-dnd'

class MyComponent {

/* ... */

}

export default DragSource(type, spec, collect)(MyComponent)

参数:

type:指定拖拽元素的类型,值的类型可以是 string、 symbol 或者 func ,只有具有相同type类型的元素才能被 drop target 所响应。

spec: 一个js对象,上面定义了一些方法,用来描述 drag source 如何对拖动事件进行响应。

beginDrag(props, monitor, component): 必填项。当拖拽开始的时候,这个方法就会被调用,这个方法必须要返回一个js 对象来描述被拖拽的元素,比如返回一个 { id: props.id },通过monitor.getItem() 方法可以获取到返回结果。

endDrag(props, monitor, component): 非必填项。当拖拽停止的时候,这个方法会被调用,通过 monitor.didDrop() 可以判断 drag source 是否已经被 drop target 处理完毕。如果在 drop target 的 drop 方法中返回了一个对象,在这里可以通过 monitor.getDropResult() 获取到返回结果。

canDrag(props, monitor): 可选参数。可以指定当前的拖拽操作是否被允许。

isDragging(props, monitor): 可选参数。拖拽时触发的事件,注意,在这个方法里面不能再调用 monitor.isDragging()。

方法中的参数解释:

- props:当前组件的 `props` 参数。

- monitor:一个 `DragSourceMonitor` 实例。通过它可以获取当前的拖拽信息,比如可以获取当前被拖拽的项目及其类型,当前和初始坐标和偏移,以及它是否已被删除。

- component:是组件的实例。使用它可以访问DOM元素来进行位置或大小测量,或调用组件里面定义的方法,或者进行 `setState` 操作。有时候在 isDragging、 canDrag 方法里可能获取不到 `component` 这个参数,因为它们被调用时实例可能不可用。

collect: 必填项,把拖拽过程中需要的信息注入组件的 props,接收两个参数 connect 和 monitor。

connect: DragSourceConnector 的实例,包括 dragPreview() 和 dragSource() 两个方法,常用的是 dragSource() 这个方法。

dragSource: 返回一个函数,传递给组件用来将 source DOM 和 React DnD Backend 连接起来。

dragPreview: 返回一个函数,传递给组件用来将拖动时预览的 DOM 节点 和 React DnD Backend 连接起来。

monitor: DragSourceMonitor 的实例,包含的具体方法可以参考这里。

DropTarget

DropTarget 是一个高阶组件,被 DropTarget 包裹的组件能够放置拖拽组件,能够对 hover 或者 dropped 事件作出响应。

基本用法:

import { DropTarget } from 'react-dnd'

class MyComponent {

/* ... */

}

export default DropTarget(types, spec, collect)(MyComponent)

参数:

types: 指定拖拽元素的类型,值的类型可以是 string、 symbol 或者 array ,drop target 只接受具有相同 type 类型的 drag source。

spec: 一个js对象,上面定义了一些方法,描述了拖放目标对拖放事件的反应。

drop(props, monitor, component): 可选参数。当item被放置到目标元素上时会被调用。如果这个方法返回的是一个js对象,在 drag source 的 endDrag 方法里面,调用 monitor.getDropResult() 可以获得返回结果。

hover(props, monitor, component): 可选参数。当item经过 drop target 的时候被调用。可以通过 monitor.isOver({ shallow: true }) 方法来检查悬停是仅发生在当前目标上还是嵌套上。

canDrop(props, monitor): 可选参数。这个方法可以用来检测 drop target 是否接受 item。

方法中的参数解释:

- props:当前组件的 `props` 参数。

- monitor:一个 `DropTargetMonitor` 实例。通过它可以获取当前的拖拽信息,比如可以获取当前被拖拽的项目及其类型,当前和初始坐标和偏移,以及它是否已被删除。

- component:是组件的实例。使用它可以访问DOM元素来进行位置或大小测量,或调用组件里面定义的方法,或者进行 `setState` 操作。有时候在 isDragging、 canDrag 方法里可能获取不到 `component` 这个参数,因为它们被调用时实例可能不可用。

collect: 必填项,把拖拽过程中需要的信息注入组件的 props,接收两个参数 connect 和 monitor。

connect: DropTargetConnector 的实例,包括 dropTarget 一个方法。

dropTarget: 返回一个函数,传递给组件用来将 source DOM 和 React DnD Backend 连接起来。

monitor: DropTargetMonitor 的实例,包含的具体方法可以参考这里。

DragDropContext && DragDropContextProvider

使用 DragSource 和 DropTarget 包裹的组件必须放置在 DragDropContext 或者 DragDropContextProvider 组件内部。

基本用法:

import Backend from 'react-dnd-html5-backend'

import { DndProvider } from 'react-dnd'

export default function MyReactApp() {

return (

/* your drag-and-drop application */

)

}

参数:

backend: 必填项。HTML5 DnD API 兼容性不怎么样,并且不适用于移动端,所以干脆把 DnD 相关具体DOM事件抽离出去,单独作为一层,即 Backend,我们可以根据 React DnD提供的约定协议定义自己的 Backend。

示例

了解了上述 API 的基本使用,现在我们就来实现下开头的demo。

本示例是基于 create-react-app 开发的,通过create-react-app的CLI工具创建我们的demo工程:

$ create-react-app react-dnd-demo

src/index.js

import React from 'react'

import ReactDOM from 'react-dom'

import Container from './Container'

import { DndProvider } from 'react-dnd'

import Backend from 'react-dnd-html5-backend'

function App() {

return (

)

}

const rootElement = document.getElementById('root')

ReactDOM.render(, rootElement)

src/Container.js

import React from 'react';

import { DropTarget } from 'react-dnd';

import DraggableBox from './DraggableBox';

import Types from './types'

const styles = {

width: '500px',

height: '300px',

position: 'relative',

border: '1px solid black',

}

@DropTarget(

Types.Box,

{

drop: (props, monitor, component) => {

if(!component) {

return;

}

const delta = monitor.getDifferenceFromInitialOffset();

const item = monitor.getItem();

const left = Math.round(delta.x + item.left);

const top = Math.round(delta.y + item.top);

component.moveBox(item.id, left, top);

},

},

(connect, monitor) => ({

connectDropTarget: connect.dropTarget(),

isOver: monitor.isOver(),

canDrop: monitor.canDrop(),

})

)

class Container extends React.Component {

state = {

boxes: {

a: { top: 20, left: 80, title: 'Drag me around' },

b: { top: 180, left: 20, title: 'Drag me too' },

},

}

moveBox = (id, left, top) => {

const { boxes } = this.state;

this.setState({

boxes: {

...boxes,

[id]: {

...boxes[id],

left,

top

}

}

})

}

render() {

const { isOver, canDrop, connectDropTarget} = this.props;

const { boxes } = this.state;

const isActive = isOver && canDrop;

let backgroundColor = '#ccc';

// 拖拽组件此时正处于 drag target 区域时,当前组件背景色变为 darkgreen

if (isActive) {

backgroundColor = '#453467';

}

console.log('qqqq', this.state.boxes)

return connectDropTarget && connectDropTarget(

{Object.keys(boxes).map(item => )}

)

}

}

export default Container;

可以看到,在 drop 方法里,通过 monitor.getDifferenceFromInitialOffset() 方法计算出每次 drop 的时候,当前元素与拖拽前元素位置的偏移量,monitor.getItem() 方法可以获得当前 哪个元素被拖拽(必须要在 drag source 的 beginDrag 方法中返回),调用 component 上的 moveBox 方法重新设置拖拽之后的最新位置,从而实现元素的移动。

collect 的 connect 方法中通过 monitor.isOver() 和 monitor.canDrop() 方法将 isOver 和 canDrop 参数传递到组件的 props 中来判断当前组件是否处于拖拽状态中,这里可以用来设置拖拽时容器的背景色。

这里有个细节需要注意的是,Container 容器的 position 属性被设置为了 relative,这样被拖拽的元素就会相对于该容器进行定位。

src/DraggableBox.js

import React from 'react';

import { DragSource } from 'react-dnd';

import Box from './Box';

import Types from './types'

@DragSource(

Types.Box,

{

beginDrag: (props) => {

const { id, title, left, top } = props

return { id, title, left, top }

}

},

(connect, monitor)=> ({

connectDragSource: connect.dragSource(),

isDragging: monitor.isDragging(),

})

)

class DraggableBox extends React.Component {

getStyle = () => {

const { left, top } = this.props;

const transform = `translate(${left}px, ${top}px)`

return {

position: 'absolute',

transform,

}

}

render() {

const { connectDragSource } = this.props;

return connectDragSource(

)

}

}

export default DraggableBox;

beginDrag 方法必须返回一个对象,以前在 drop 方法中获取到当前被拖拽组件的信息。positon 属性必须被设置为 absolute,以方便相对容器进行定位。元素的移动是通过 css 的 transform 属性进行控制的。

src/Box.js

import React from 'react';

const styles = {

border: '1px dashed gray',

backgroundColor: 'white',

padding: '0.5rem 1rem',

marginRight: '1.5rem',

marginBottom: '1.5rem',

cursor: 'move',

display: 'inline-block'

}

class Box extends React.Component {

render() {

const { title, left, right } = this.props;

return (

{title}

)

}

}

export default Box;

总结

关于 React DnD 的介绍,这里只是做了一个基本介绍,更多的示例大家可以参考官网,本示例的代码大家在这里可以找到。

createdroptargets_拖拽神器React DnD你真的了解了吗?相关推荐

  1. antd 日期选择框如何提交_基于Ant Design的Modal组件来实现一个可拖拽的React模态框...

    引言 写这篇文章的原因是因为在项目中用到了Antd Design的React组件,当有业务需求需要用到模态框的时候遇到了一些小问题,Antd的模态框Modal组件时不能拖拽的,一般情况下不可拖拽也没什 ...

  2. 拖拽批量上传图片如何保证 顺序_图片压缩神器和图片分割工具,美工设计和运营终于得救了...

    想要快速互联网干货技巧? 请(置顶)星标我们 好不容易写好文案,设计好长图海报,上传到微信公众号,竟然提示上传图片体积不得超过5M,怎么办?怎么办? 不得已,只好又返回Ps里降低图片品质,换成有损格式 ...

  3. React造轮子:拖拽排序组件「Dragact」

    先来一张图看看: 项目地址:Github地址 (无耻求星!) 在线观看(第一次加载需要等几秒):预览地址 说起来不容易,人在国外没有过年一说,但是毕竟也是中国年,虽然不放假,但是家里总会主内一顿丰盛的 ...

  4. react 拖拽连接插件_一款精美的 react 后台管理系统

    众里寻他千百度,蓦然回首,那人却在,灯火阑珊处.这么好的react开源项目,值得珍藏了! 项目依赖模块 项目是用create-react-app创建的,依赖包如下: react(是一个用于构建用户界面 ...

  5. react实现的点击拖拽元素效果

    之前用vue做日程管理组件的时候,用到了点击拖拽的效果,即点击元素,鼠标移动到哪里,元素移动到哪里,鼠标松开,拖拽停止,现在在弄react,于是也在想实现这个效果,经过一番折腾,效果出来了,代码如下: ...

  6. 现代 React Web 开发实战——kanban实现卡片拖拽

    前提摘要: 学习宋一玮 React 新版本 + 函数组件 &Hooks 优先 开篇就是函数组件+Hooks 实现的效果如下: 学到第11篇了 照葫芦画瓢,不过老师在讲解的过程中没有考虑拖拽目标 ...

  7. 拖拽式创建小程序原型 - 小piu神器 - 腾讯lowCode - 软件开发

    免费的拖拽式小程序.App原型设计稿生成器(小piu) - 感谢神器! https://www.xiaopiu.com/ 收费的微信小程序 拖拽生成开发: 腾讯云微搭低代码 WeDa https:// ...

  8. React.js实现原生js拖拽效果及思考

    一.起因&思路 不知不觉,已经好几天没写博客了...近来除了研究React,还做了公司官网... 一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨.所以就用react来实现这个拖 ...

  9. react 实现自定义拖拽hook

    前沿 最近发现公司的产品好几个模块用到了拖拽功能,之前拖拽组件是通过Html5 drag Api 实现的但体验并不是很好,顺便将原来的拖拽组建稍做修改,写一个自定义hook,方便大家使用拖拽功能. 正 ...

  10. react拖拉流程图_react 拖拽组件 自由拖拽,垂直水平拖拽

    import React, { useEffect, useState } from "react" import { Tree, message } from 'antd'; / ...

最新文章

  1. 阿里云MVP北京闭门会圆满落幕 多把“利剑”助力开发者破阵蜕变
  2. Codeblocks更改编译器为VC++6.0
  3. numpy列相加_Python数据分析入门:NumPy基础:数组与向量化计算
  4. 我的世界服务器linux加mod,在Linux下搭建带MOD 我的世界(Minecraft)服务器
  5. 简易 Vue 构建--终
  6. 论文阅读之ALBERT
  7. 踩坑日记(一):半夜打DBA电话解决数据库连接超时自动回滚问题
  8. 在线密码管理器LastPass遭入侵 官方建议修改主密码
  9. IT公司面试 逻辑题目
  10. 最简单的单片机c语言程序,单片机的C语言编程基础知识(初学注意)
  11. Notepad++安装JsonViewer插件
  12. 今日头条怎么引流?头条暴力引流方法
  13. E+H悬浮物测量传感器CUS51D-AAD1A3
  14. OKR工作法的简单总结
  15. JavaSE基础知识回顾
  16. linux命令查看服务器的型号、序列号、内存插槽数
  17. 微软气坏了!Windows 惨遭抄袭,这款系统简直超越正品
  18. ZZULIOJ 1787 生化危机 (vector+DFS)
  19. 计算机网络基础北邮,北邮 大三下 计算机网络技术实践 实验五_图文
  20. 计算机二级vf上机题库,计算机二级VF上机题库

热门文章

  1. SCUT - 12 - 西方国家 - 矩阵快速幂
  2. [Luogu P2522] [HAOI2011]Problem b (莫比乌斯反演)
  3. JVM内存模型及垃圾回收机制
  4. 14.19 InnoDB and MySQL Replication InnoDB 和MySQL 复制:
  5. eclipse重定向输入输出到文件
  6. silverlight 碰撞检测
  7. 线程等待与唤醒c语言,c – 在pthreads中唤醒单个线程而不是忙等待
  8. ByteBuf详解和Netty中的拆包粘包原理解析
  9. SpringApplication run方法解析:SpringApplicationRunListeners(二)
  10. 一图助你搞明白Spring应用上下文初始化流程!