首頁 >web前端 >js教程 >React DND實現的卡片排序功能(程式碼範例)

React DND實現的卡片排序功能(程式碼範例)

不言
不言轉載
2018-11-17 15:19:175812瀏覽

這篇文章帶給大家的內容是關於React DND實現的卡片排序功能(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

在公司初學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 (
        <div>
            {CardList.map((item,index) => {
                return(
                    <carditem></carditem>
                )
            })}
        </div>
    );
  }
}

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(
            <div>
                <card>
                    <p>{this.props.content}</p>
                </card>
            </div>
        )
    }
}

export default CardItem

好了,卡片寫完成了,現在運行一下我們的項目,看看效果

$ npm start or yarn start

React DND實現的卡片排序功能(程式碼範例)

#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包裹的子元件都可以使用DropTarget和DragSource了,我們現在在子元件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( <div> 
                <card>
                    <p>{this.props.content}</p>
                </card>
            </div> )
        )
    }
}

// 使组件连接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 decorator, stem.
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);

現在我們已經完成了一個拖曳效果的實現,現在我們來看一下效果

React DND實現的卡片排序功能(程式碼範例)

可以很明顯的看到拖曳帶來的效果,接下來我們要完成拖曳放置後的排序函數。
我們將排序函數放在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
        }
        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
        })
    };
    
    
    /* ...
           */
           
    //添加传递参数传递函数
    <carditem></carditem>

好了,現在我們已經完成了一個卡片排序功能的小demo,讓我們來看看效果吧!

React DND實現的卡片排序功能(程式碼範例)

#

以上是React DND實現的卡片排序功能(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除