溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

React DND如何實(shí)現(xiàn)的卡片排序功能

發(fā)布時(shí)間:2020-10-19 14:32:19 來源:億速云 閱讀:182 作者:小新 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)React DND如何實(shí)現(xiàn)的卡片排序功能,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

在公司初學(xué)react,其中一個(gè)要求讓我實(shí)現(xiàn)拖拽排序的功能,完成之后記錄一下實(shí)現(xiàn)方法,采用antd和reactDND來實(shí)現(xiàn)這個(gè)功能。

一、環(huán)境搭建

首先,使用 create-react-app 腳手架創(chuàng)建一個(gè)最基本的react項(xiàng)目。

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

OK,構(gòu)建好了react項(xiàng)目,然后我們引入antd,和react-dnd

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

引用完antd后可以按照antd官網(wǎng)上的方法完成按需加載。

二、功能實(shí)現(xiàn)

我們先使用antd寫出一個(gè)簡單的卡片列表,修改項(xiàng)目目錄的APP.js和App.css文件,新建一個(gè)文件CardItem.js

//App.js

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

const CardList = [{ //定義卡片內(nèi)容
    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 className='card'>
            {CardList.map((item,index) => {
                return(
                    <CardItem //向次級界面?zhèn)鬟f參數(shù)
                        key={item.id}
                        title={item.title}
                        content={item.content}
                        index={index}
                    />
                )
            })}
        </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
                    title={this.props.title}
                    style={{ width: 300 }}
                >
                    <p>{this.props.content}</p>
                </Card>
            </div>
        )
    }
}

export default CardItem

好了,卡片編寫完成了,現(xiàn)在運(yùn)行一下我們的項(xiàng)目,看一下效果

$ npm start or yarn start

React DND如何實(shí)現(xiàn)的卡片排序功能

OK,編寫完成,我們現(xiàn)在要做的就是使用react-dnd完成卡片的拖拽排序,使得firstCard,secondCard,thirdCard可以隨意的交換。

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

  • DragDropContext 用于包裝拖拽根組件,DragSourceDropTarget 都需要包裹在DragDropContex內(nèi)

  • DropTarget 用于包裝你需要拖動(dòng)的組件,使組件能夠被拖拽

  • DragSource 用于包裝接收拖拽元素的組件,使組件能夠放置

理解了這些API的作用,一個(gè)卡片排序的構(gòu)建思路大體就浮現(xiàn)出來了,怎么樣實(shí)現(xiàn)一個(gè)卡片排序,其實(shí)很簡單,就是把卡片列表中的每一個(gè)卡片都設(shè)置為DropTargetDragSource,最后在拖拽結(jié)束的時(shí)候進(jìn)行卡片之間的重排序,完成這一功能的實(shí)現(xiàn)。下面我們就來一步一步的實(shí)現(xiàn)它。

首先設(shè)定DragDropContext,在App.js中引入 react-dndreact-dnd-html5-backend(先npm install這個(gè)插件)

//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);

好了,現(xiàn)在被App.js所包裹的子組件都可以使用DropTarget和DragSource了,我們現(xiàn)在在子組件CardItem中設(shè)定react-dnd使得卡片現(xiàn)在能夠有拖動(dòng)的效果。

//CardItem.js

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


const Types = { // 設(shè)定類型,只有DragSource和DropTarget的類型相同時(shí),才能完成拖拽和放置
    CARD: 'CARD'
};

//DragSource相關(guān)設(shè)定
const CardSource = {  //設(shè)定DragSource的拖拽事件方法
    beginDrag(props,monitor,component){ //拖拽開始時(shí)觸發(fā)的事件,必須,返回props相關(guān)對象
        return {
            index:props.index
        }
    },
    endDrag(props, monitor, component){
      //拖拽結(jié)束時(shí)的事件,可選
    },
    canDrag(props, monitor){
      //是否可以拖拽的事件??蛇x
    },
    isDragging(props, monitor){
      // 拖拽時(shí)觸發(fā)的事件,可選
    }
};

function collect(connect,monitor) { //通過這個(gè)函數(shù)可以通過this.props獲取這個(gè)函數(shù)所返回的所有屬性
    return{
        connectDragSource:connect.dragSource(),
        isDragging:monitor.isDragging()
    }
}

//DropTarget相關(guān)設(shè)定
const CardTarget = {
    drop(props, monitor, component){ //組件放下時(shí)觸發(fā)的事件
        //...
    },
    canDrop(props,monitor){ //組件可以被放置時(shí)觸發(fā)的事件,可選
        //...
    },
    hover(props,monitor,component){ //組件在target上方時(shí)觸發(fā)的事件,可選
        //...
    },
    
};

function collect1(connect,monitor) {//同DragSource的collect函數(shù)
    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; //當(dāng)被拖拽時(shí)呈現(xiàn)透明效果

        return connectDragSource( //使用DragSource 和 DropTarget
            connectDropTarget( <div> 
                <Card
                    title={this.props.title}
                    style={{ width: 300 ,opacity}}
                >
                    <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)

最后這個(gè)連接方法我參考了 reactDND官網(wǎng) 的說明,你可以去 lodash.flow的官網(wǎng) 進(jìn)行查看并下載。
當(dāng)然你也可以選擇構(gòu)造器的方法進(jìn)行引用,如@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);

現(xiàn)在我們已經(jīng)完成了一個(gè)拖拽效果的實(shí)現(xiàn),現(xiàn)在我們來看一下效果

React DND如何實(shí)現(xiàn)的卡片排序功能

可以很明顯的看到拖拽帶來的效果,接下來我們要完成拖拽放置后的排序函數(shù)。
我們將排序函數(shù)放在App.js當(dāng)中,在CardItem.js中的CardTarget構(gòu)造方法中的hover函數(shù)中進(jìn)行調(diào)用,接下來看具體的實(shí)現(xiàn)方法.

//CardItem.js

const CardTarget = {
    hover(props,monitor,component){
        if(!component) return null; //異常處理判斷
        const dragIndex = monitor.getItem().index;//拖拽目標(biāo)的Index
        const hoverIndex = props.index; //放置目標(biāo)Index
        if(dragIndex === hoverIndex) return null;// 如果拖拽目標(biāo)和放置目標(biāo)相同的話,停止執(zhí)行
        
        //如果不做以下處理,則卡片移動(dòng)到另一個(gè)卡片上就會(huì)進(jìn)行交換,下方處理使得卡片能夠在跨過中心線后進(jìn)行交換.
        const hoverBoundingRect = (findDOMNode(component)).getBoundingClientRect();//獲取卡片的邊框矩形
        const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2;//獲取X軸中點(diǎn)
        const clientOffset = monitor.getClientOffset();//獲取拖拽目標(biāo)偏移量
        const hoverClientX = (clientOffset).x - hoverBoundingRect.left;
        if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) { // 從前往后放置
            return null
        }
        if (dragIndex > hoverIndex && hoverClientX > hoverMiddleX) { // 從后往前放置
            return null
        }
        props.DND(dragIndex,hoverIndex); //調(diào)用App.js中方法完成交換
        monitor.getItem().index = hoverIndex; //重新賦值index,否則會(huì)出現(xiàn)無限交換情況
    }
}
//App.js

    handleDND = (dragIndex,hoverIndex) => {
        let CardList = this.state.CardList;
        let tmp = CardList[dragIndex] //臨時(shí)儲(chǔ)存文件
        CardList.splice(dragIndex,1) //移除拖拽項(xiàng)
        CardList.splice(hoverIndex,0,tmp) //插入放置項(xiàng)
        this.setState({
            CardList
        })
    };
    
    
    /* ...
           */
           
    //添加傳遞參數(shù)傳遞函數(shù)
    <CardItem //向次級界面?zhèn)鬟f參數(shù)
        key={item.id}
        title={item.title}
        content={item.content}
        index={index}
        onDND={this.handleDND}
    />

好了,現(xiàn)在我們已經(jīng)完成了一個(gè)卡片排序功能的小demo,讓我們來看一下效果吧!

React DND如何實(shí)現(xiàn)的卡片排序功能

關(guān)于React DND如何實(shí)現(xiàn)的卡片排序功能就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI