资讯专栏INFORMATION COLUMN

使用React DND 完成一个简单的卡片排序功能

edgardeng / 1085人阅读

摘要:简介在公司初学其中一个要求让我实现拖拽排序的功能完成之后记录一下实现方法,采用和来实现这个功能。一环境搭建首先,使用脚手架创建一个最基本的项目。

简介

在公司初学react,其中一个要求让我实现拖拽排序的功能,完成之后记录一下实现方法,采用antd和reactDND来实现这个功能。

一、环境搭建

首先,使用 create-react-app 脚手架创建一个最基本的react项目。

npm install -g create-react-app
create-react-app my-app
cd my-app

OK,构建好了react项目,然后我们引入antd,和react-dnd

$ yarn add antd
$ yarn add react-dnd
$ yarn add react-dnd-html5-backend

引用完antd后可以按照antd官网上的方法完成按需加载。

二、功能实现

我们先使用antd写出一个简单的卡片列表,修改项目目录的APP.js和App.css文件,新建一个文件CardItem.js

//App.js

import React, { Component } from "react";
import CardItem from "./CardItem"
import "./App.css";

const CardList = [{ //定义卡片内容
    title:"first Card",
    id:1,
    content:"this is first Card"
  },{
    title:"second Card",
    id:2,
    content:"this is second Card"
  },{
    title:"Third Card",
    id:3,
    content:"this is Third Card"
  }
];
class App extends Component {
  state = {
    CardList
  }; 
  render() {
    return (
        
{CardList.map((item,index) => { return( ) })}
); } } export default App; //App.css .card{ display: flex; margin: 50px; } .card div{ margin-right: 20px; } //CardItem.js import React, { Component } from "react"; import {Card} from "antd" class CardItem extends Component{ render(){ return(

{this.props.content}

) } } export default CardItem

好了,卡片编写完成了,现在运行一下我们的项目,看一下效果

$ npm start or yarn start

OK,编写完成,我们现在要做的就是使用react-dnd完成卡片的拖拽排序,使得firstCard,secondCard,thirdCard可以随意的交换。

react-dnd中提供了DragDropContext,DragSource,DropTarget 3种API;

DragDropContext 用于包装拖拽根组件,DragSourceDropTarget 都需要包裹在DragDropContex

DropTarget 用于包装你需要拖动的组件,使组件能够被拖拽

DragSource 用于包装接收拖拽元素的组件,使组件能够放置

理解了这些API的作用,一个卡片排序的构建思路大体就浮现出来了,怎么样实现一个卡片排序,其实很简单,就是把卡片列表中的每一个卡片都设置为DropTargetDragSource,最后在拖拽结束的时候进行卡片之间的重排序,完成这一功能的实现。下面我们就来一步一步的实现它。

首先设定DragDropContext,在App.js中引入 react-dndreact-dnd-html5-backend(先npm install这个插件)

//App.js

import React, { Component } from "react";
import CardItem from "./CardItem"
+ import {DragDropContext} from "react-dnd"
+ import HTML5Backend from "react-dnd-html5-backend"
import "./App.css";

/*..
..*/

- export default App;
+ export default DragDropContext(HTML5Backend)(App);

好了,现在被App.js所包裹的子组件都可以使用DropTargetDragSource了,我们现在在子组件CardItem中设定react-dnd使得卡片现在能够有拖动的效果。

//CardItem.js

import React, { Component } from "react";
import {Card} from "antd"
+ import { //引入react-dnd
    DragSource,
    DropTarget,
} from "react-dnd"


const Types = { // 设定类型,只有DragSource和DropTarget的类型相同时,才能完成拖拽和放置
    CARD: "CARD"
};

//DragSource相关设定
const CardSource = {  //设定DragSource的拖拽事件方法
    beginDrag(props,monitor,component){ //拖拽开始时触发的事件,必须,返回props相关对象
        return {
            index:props.index
        }
    },
    endDrag(props, monitor, component){
      //拖拽结束时的事件,可选
    },
    canDrag(props, monitor){
      //是否可以拖拽的事件。可选
    },
    isDragging(props, monitor){
      // 拖拽时触发的事件,可选
    }
};

function collect(connect,monitor) { //通过这个函数可以通过this.props获取这个函数所返回的所有属性
    return{
        connectDragSource:connect.dragSource(),
        isDragging:monitor.isDragging()
    }
}

//DropTarget相关设定
const CardTarget = {
    drop(props, monitor, component){ //组件放下时触发的事件
        //...
    },
    canDrop(props,monitor){ //组件可以被放置时触发的事件,可选
        //...
    },
    hover(props,monitor,component){ //组件在target上方时触发的事件,可选
        //...
    },
    
};

function collect1(connect,monitor) {//同DragSource的collect函数
    return{
        connectDropTarget:connect.dropTarget(),
        isOver:monitor.isOver(), //source是否在Target上方
        isOverCurrent: monitor.isOver({ shallow: true }), 
        canDrop: monitor.canDrop(),//能否被放置
        itemType: monitor.getItemType(),//获取拖拽组件type
    }
}

class CardItem extends Component{

    render(){
        const { isDragging, connectDragSource, connectDropTarget} = this.props;
        let opacity = isDragging ? 0.1 : 1; //当被拖拽时呈现透明效果

        return connectDragSource( //使用DragSource 和 DropTarget
            connectDropTarget( 

{this.props.content}

) ) } } // 使组件连接DragSource和DropTarget let flow = require("lodash.flow"); export default flow( DragSource(Types.CARD,CardSource,collect), DropTarget(Types.CARD,CardTarget,collect1) )(CardItem)

最后这个连接方法我参考了 reactDND官网 的说明,你可以去 lodash.flow的官网 进行查看并下载。
当然你也可以选择构造器的方法进行引用,如@DragSource(type, spec, collect)@DropTarget(types, spec, collect).

Even if you don"t plan to use decorators, the partial application can
still be handy, because you can combine several DragSource and
DropTarget declarations in JavaScript using a functional composition
helper such as _.flow. With decorators, you can just stack the
decorators to achieve the same effect.
import { DragSource, DropTarget } from "react-dnd";
import flow from "lodash/flow";

class YourComponent {
  render() {
    const { connectDragSource, connectDropTarget } = this.props
    return connectDragSource(connectDropTarget(
      /* ... */
    ))
  }
}

export default flow(
  DragSource(/* ... */),
  DropTarget(/* ... */)
)(YourComponent);

现在我们已经完成了一个拖拽效果的实现,现在我们来看一下效果

可以很明显的看到拖拽带来的效果,接下来我们要完成拖拽放置后的排序函数。
我们将排序函数放在App.js当中,在CardItem.js中的CardTarget构造方法中的hover函数中进行调用,接下来看具体的实现方法.

//CardItem.js

const CardTarget = {
    hover(props,monitor,component){
        if(!component) return null; //异常处理判断
        const dragIndex = monitor.getItem().index;//拖拽目标的Index
        const hoverIndex = props.index; //放置目标Index
        if(dragIndex === hoverIndex) return null;// 如果拖拽目标和放置目标相同的话,停止执行
        
        //如果不做以下处理,则卡片移动到另一个卡片上就会进行交换,下方处理使得卡片能够在跨过中心线后进行交换.
        const hoverBoundingRect = (findDOMNode(component)).getBoundingClientRect();//获取卡片的边框矩形
        const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2;//获取X轴中点
        const clientOffset = monitor.getClientOffset();//获取拖拽目标偏移量
        const hoverClientX = (clientOffset).x - hoverBoundingRect.left;
        if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) { // 从前往后放置
            return null
        }
        if (dragIndex > hoverIndex && hoverClientX > hoverMiddleX) { // 从后往前放置
            return null
        }
        props.DND(dragIndex,hoverIndex); //调用App.js中方法完成交换
        monitor.getItem().index = hoverIndex; //重新赋值index,否则会出现无限交换情况
    }
}
//App.js

    handleDND = (dragIndex,hoverIndex) => {
        let CardList = this.state.CardList;
        let tmp = CardList[dragIndex] //临时储存文件
        CardList.splice(dragIndex,1) //移除拖拽项
        CardList.splice(hoverIndex,0,tmp) //插入放置项
        this.setState({
            CardList
        })
    };
    
    
    /* ...
           */
           
    //添加传递参数传递函数
    

好了,现在我们已经完成了一个卡片排序功能的小demo,让我们来看一下效果吧!

END

本人初学前端不久,刚接触react相关,这篇文章也是用于记录一下自己工作时用到的一些小功能,本文参考了

强大的拖拽组件:React DnD 的使用

和 reactDND 官网上的相关例子,一些更复杂的情况大家也可以去reactDND的官网上查看.

源代码地址:https://github.com/wzb0709/Ca...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108662.html

相关文章

  • 使用 Drag and Drop 给Web应用提升交互体验

    摘要:注意点在鼠标操作拖放期间,有一些事件可能触发多次,比如和。可拖拽元素,建议使用,设定可拖拽元素的鼠标游标,提升交互。在中使用拖拽中使用可以直接绑定到组件上。 什么是 Drag and Drop (拖放)? 简单来说,HTML5 提供了 Drag and Drop API,允许用户用鼠标选中一个可拖动元素,移动鼠标拖放到一个可放置到元素的过程。 我相信每个人都或多或少接触过拖放,比如浏览...

    legendmohe 评论0 收藏0
  • 页面搭建工具总结及架构思考

    摘要:在初步完成了在线流程图编辑工具之后又接到了在线搭建页面工具的需求刚开始其实并不想接项目因为从历史以及现实原因来看个性化及动态渲染都是很难解决的痛点各种页面搭建工具的不温不火早已说明了这条路并没有这么好走但从另一个方面来说既然有了这样的需求那 在初步完成了在线流程图编辑工具之后,又接到了在线搭建页面工具的需求,刚开始其实并不想接项目,因为从历史以及现实原因来看,个性化及动态渲染都是很难解决的痛...

    William_Sang 评论0 收藏0
  • React-sortable-hoc 结合 hook 实现 Draggin 和 Droppin

    摘要:启动项目教程最终的目的是构建一个带有趣的应用程序来自,可以在视口周围拖动。创建组件,添加样式和数据为简单起见,我们将在文件中编写所有样式。可以看出,就是在当前的外层包裹我们所需要实现的功能。现在已经知道如何在项目中实现拖放 翻译:https://css-tricks.com/draggi... React 社区提供了许多的库来实现拖放的功能,例如 react-dnd, react-b...

    molyzzx 评论0 收藏0
  • 写了一个简单、灵活React标签组件

    摘要:最近的项目里需要实现一个标签组件,内部标签可任意拖动排序。网上搜了一圈发现几乎没有现成的基于的组件能很好的满足需求。 最近的项目里需要实现一个标签组件,内部标签可任意拖动排序。网上搜了一圈发现几乎没有现成的基于react的组件能很好的满足需求。 较为知名的是react-dnd,然而它似乎只支持把一个元素移到固定的位置,我需要的是一个标签可以移动到任意位置的两个标签之间(每个标签长度不固...

    arashicage 评论0 收藏0
  • React-dnd实现拖拽,最简单代码,直接可以跑

    摘要:不多说,直接上代码需要版本貌似与方法有关类似的高阶组件包裹被拖的元素高阶组件包裹被释放的元素这个库是必须的,类似于的合成事件解决浏览器差异,抽象事件操作为可以处理的 不多说,直接上代码 react-dnd 需要react版本 > 16.6 ,貌似与react.memo方法有关 import React from react // DragDropContext 类似React的Co...

    xiaokai 评论0 收藏0

发表评论

0条评论

edgardeng

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<