安装React-dnd库
$ yarn add react-dnd
$ yarn react-dnd-html5-backend
在需要拖动的组件中引入这两个库( 这里以main.js,需要拖拽那个组件就使用DndProvider包裹即可 )
import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-html5-backend'
<DndProvider backend={HTML5Backend}>
<App/>
</DndProvider>
App.js代码
import React, { useState } from 'react'
import Cart from './components/cart'
export default function App () {
const [carts, setCarts] = useState([
{ id: 1, text: '卡片1' },
{ id: 2, text: '卡片2' },
{ id: 3, text: '卡片3' },
])
const moveCart = (dragIndex, hoverIndex) => {
// 获取被移动的元素
const dragEle = carts[dragIndex]
const newCarts = [...carts]
newCarts.splice(dragIndex, 1)
newCarts.splice(hoverIndex, 0, dragEle)
setCarts(newCarts)
}
return (
<div>
{carts.map((item, index) => {
return <Cart key={item.id} text={item.text} index={index} moveCart={moveCart} />
})}
</div>
)
}
src/components/cart.js
import React, { useRef } from 'react'
import { useDrag, useDrop } from 'react-dnd'
const style = {
width: '300px',
backgroundColor: 'red',
padding: '10px',
margin: '5px',
}
export default function Cart ({ text, index, moveCart }) {
const [, drop] = useDrop({
// 一个字符串,放置目标,只会对指定类型的拖动源发生反应
accept: 'cart',
collect: () => ({}),
hover (item, monitor) {
// 获取当前拖动元素的索引
const dropIndex = item.index
// 当前正在hover的index
const hoverIndex = index
// 表示拖动元素和放置的元素的位置一样
if (dropIndex === hoverIndex) return
// 获取元素的高度
const { bottom, top } = ref.current.getBoundingClientRect()
// 获取拖动元素高度的一半
const halfOfHoverHeight = (bottom - top) / 2
// 获取鼠标的位置
const { y } = monitor.getClientOffset()
// 鼠标移动的高度
const hoverClientY = y - top
// 判断移动的高度
if ((dropIndex < hoverIndex && hoverClientY > halfOfHoverHeight) || (dropIndex > hoverIndex && hoverClientY < halfOfHoverHeight)) {
// 调用方法 让组件重新渲染
moveCart(dropIndex, hoverIndex)
// 移动后位置的索引 = 当前鼠标hover元素的索引
item.index = hoverIndex
}
}
})
let ref = useRef() // { current:null }
// 结构出的 isDragging 是hooks提供的一种方法 将拖动源链接到react-dnd系统中
// drag 是DragSource 是拖动源的连接器 链接真是DOM
let [{ isDragging }, drag] = useDrag({
// type 表示属性 表示拖拽的类型
type: 'cart',
// item 用来描述拖动源的一个普通js对象
item: () => { return { text, index } },
// collect 收集 是用来收集属性的 返回一个js对象,并且将返回值合并到组件的属性中
// monitor 监视器 里面存放的是拖动的状态,当状态发生改变时会通知组件,重新获取属性并且组件进行刷新
collect: (monitor) => {
return {
isDragging: monitor.isDragging(),
}
}
})
// 当开始拖动时 isDragging 为true 设置其透明度
let opacity = isDragging ? .1 : 1
// 使用drag方法 将拖动源链接到react-dnd系统中
drag(ref)
drop(ref)
return (
<div ref={ref} style={{ ...style, opacity }}>{text}</div>
)
}
最后看下效果(有点模糊将就看)