react-dnd实现卡片拖拽排序

最近工作中遇到一个需求,需要对管理系统中的图片进行拖拽排序的功能

具体用到了react-dnd来实现

GIthub地址:https://github.com/goccult/drag-sort-test

 

下面是一个利用react-dnd实现的拖拽排序小Demo

首先在react项目中执行以下命令

yarn add react-dnd

yarn add react-dnd-html5-backen

// CardTable.js
import CardItem from './CardItem'
import HTML5Backend from 'react-dnd-html5-backend'
import React from 'react';
import { DndProvider } from 'react-dnd'
import './CardTable.less';

class CardTable extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      cardList: [],
      newRender: false,
    }
  }

  // 初始化数据
  componentDidMount = () => {
    let cardList = []
    for(let i = 0; i < 32; i++) {
      cardList.push(i)
    }
    this.setState({
      cardList,
    })
  }

  // 卡片重新排序
  moveSort = (dragIndex, hoverIndex) => {
    const cardList = this.state.cardList
    const tmp = cardList[dragIndex]
    cardList.splice(dragIndex, 1)
    cardList.splice(hoverIndex, 0, tmp)
    this.setState({
      cardList,
    })
  }

  // 卡片重新render
  newRenderFn = () => {
    this.setState({
      newRender: !this.state.newRender,
    })
  }

  render() {
    return (
      // 使用dndprovider包裹可被拖拽的卡片
      <DndProvider backend={HTML5Backend}>
        <div className="CardTable">
          {
            this.state.cardList.map((value, index) => {
              return <CardItem 
                key={index} 
                index={index} 
                value={value}
                newRender={this.state.newRender}
                moveSort={this.moveSort}
                newRenderFn={this.newRenderFn}
              />
            })
          }
        </div>
      </DndProvider>
    );
  }
}

export default CardTable;
// CardTable.less
.CardTable {
  width: 700px;
  // max-height: 800px;
  overflow: hidden;
  display: grid;
  flex-wrap: wrap;
  justify-content: center;
  grid-template-columns: repeat(auto-fill, 100px);
  grid-gap: 20px 30px;
  grid-template-rows: 100px;
  margin: auto;
}

 

// CardItem.js

import React from 'react';
import { DragSource, DropTarget } from 'react-dnd'
import { findDOMNode } from 'react-dom'
import './CardItem.less'

let dragingIndex = '-1'
let opacity = '1'

// 被拖拽的卡片
const CardSource = {
  // 卡片开始被拖拽时触发
  beginDrag(props) {
    dragingIndex = props.value
    return {
      index: props.index,
    }
  },
  // 卡片结束拖拽时触发
  endDrag(props) {
    dragingIndex = '-1'
    opacity = '1'
    props.newRenderFn() 
  },
}

// 目标卡片
const CardTarget = {
  hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index
    const hoverIndex = props.index

    if(dragIndex === hoverIndex) return null
    const hoverBoundingRect = (findDOMNode(component)).getBoundingClientRect()
    const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2
    const clientOffset = monitor.getClientOffset()
    const hoverClientX = (clientOffset).x - hoverBoundingRect.left // 悬浮时鼠标距离目标源的左边距

    if(hoverIndex - dragIndex === 1 && hoverClientX < hoverMiddleX) {
      return null
    }
    if(dragIndex - hoverIndex === 1 && hoverClientX > hoverMiddleX) {
      return null
    }

    props.moveSort(dragIndex, hoverIndex)
    monitor.getItem().index = hoverIndex
  },
}

class CardItem extends React.Component {

  render() {
    const {connectDragSource, connectDropTarget, value} = this.props
    opacity = value === dragingIndex  ? '0.1' : '1'
    return (
      connectDragSource(connectDropTarget(
        <div className="CardItem" style={{opacity}}>
          {this.props.value}
        </div>
      ))
    );
  }
}

export default DragSource('card', CardSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging(),
}))(
  DropTarget('card', CardTarget, (connect, monitor) => ({
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
  }))(CardItem),
)
// CardItem.less

.CardItem {
  width: 100px;
  height: 100px;
  background: rgb(0, 255, 255);
  cursor: move;
}

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值