最近工作中遇到一个需求,需要对管理系统中的图片进行拖拽排序的功能
具体用到了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;
}