优势
1 .遇到一个不能忍受的问题,就是拖拽的时候那个禁止按钮无法替换。看下这个拖拽组件有什么东西是别的不能替代的
2 .React DnD建立在HTML5拖放API之上。这是一个合理的默认值,因为它可以对已拖动的DOM节点进行屏幕快照,并将其用作开箱即用的“拖动预览”。方便的是,您不必在光标移动时进行任何绘制-这尼玛就是坑,为了这一个小的牺牲很大的哦
3 .它在触摸屏上不起作用,并且与其他浏览器相比,它在IE上提供的自定义机会更少
4 .所有后端都将DOM事件转换为React DnD可以处理的内部Redux动作
5 .拖拽和被放置区域都有类型。这些类型使您可以指定兼容哪些拖动源和放置目标
6 .监控器
1 .拖放本质上是由状态的,正在进行拖动操作,或者没有,有当前类型和当前项目。或者没有
2 .所有的状态其实自己已经写好了,在collect里面收集就好了,选取自己需要的返回即可
3 .那为什么会没有当前拖放元素的位移呢。。。。
7 .连接器
1 .
最简单使用
1 .父组件:最外面包起来
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'
ReactDOM.render(
<DndProvider backend={HTML5Backend}>
<PageTwo/>
<Drop/>
</DndProvider>,
document.getElementById('root')
);
2 .可拖拽的组件
import React, { CSSProperties } from 'react';
import { useDrag } from 'react-dnd';
const style: CSSProperties = {
width: 200,
height: 50,
lineHeight: '50px',
background: 'pink',
margin: '30px auto'
}
const Box = () => {
const [, drager] = useDrag({
item: { type: 'Box' }
})
return (
<div style={ style } ref={drager}>可拖拽组件 Box</div>
)
}
export default Box;
放置拖拽的区域
import React, { CSSProperties } from 'react';
import { useDrop, DropTargetMonitor } from 'react-dnd';
const style: CSSProperties = {
width: 400,
height: 400,
margin: '100px auto',
lineHeight: '60px',
border: '1px dashed black'
}
const Dustbin = () => {
// 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
const [collectProps, droper] = useDrop({
// accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
accept: 'Box',
// collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
collect: (minoter: DropTargetMonitor) => ({
isOver: minoter.isOver()
})
})
const bg = collectProps.isOver ? 'deeppink' : 'white';
const content = collectProps.isOver ? '快松开,放到碗里来' : '将 Box 组件拖动到这里'
return (
// 将 droper 赋值给对应元素的 ref
<div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
)
}
export default Dustbin;
数据拖拽放入几个区域
box
import React from 'react';
import { useDrag } from 'react-dnd';
let listId=100000000000
// 默认的增加id其实应该是原来数组的长度+1,这里先写一个很长的,之后在尝试修改吧
// 在他这里:所有拖拽的改变数据的操作都是在数据释放的时候操作的,并不是在接受拖拽物体释放的容器上面
// 那感觉这里有点不太科学,把所有容器之间的数据流动全都绑定到一个box上面,那么就意味着一个box每多一个可以拖放的地方,就要多传一个数据和方法,这样感觉效率非常的低下。
// 还是应该在拖放区域检测真正的数据变化。
// from,index
// 在任意一个可释放拖拽的区域,只要知道即将拖拽的元素的来自的数组和在数组内的位置,就可以通过改变数组来实现视图的变化
//接下来就是另一种写法,处理数据的时候都是在拖拽区域收到有东西在这里释放的时候操作
const style = {
border: '1px dashed gray',
backgroundColor: 'white',
padding: '0.5rem 1rem',
marginRight: '1.5rem',
marginBottom: '1.5rem',
cursor: 'move',
width:'100px'
// float: 'left',
}
export interface ListData{
id:number,
category:string,
bg:string,
type:string
}
export interface ListProps{
oldCardList:ListData[],
newCardList:ListData[],
changeOldCardList:(list:ListData[])=>void,
changeNewCardList:(list:ListData[])=>void,
}
const Box=({category,bg,oldCardList,newCardList,type,changeOldCardList,changeNewCardList}:ListData&ListProps)=> {
const [{isDragging}, drager] = useDrag({
item: { type:type,name:category},
begin:()=>{
// 开始拖拽的时候,新数组来一个占位符
const useless=newCardList.find((item)=>item.id===-1)
if(!useless){
// changeNewCardList(
// [{
// bg:"aqua",
// category:"放这里",
// id:-1,
// },
// ...newCardList]
// )
// 通过这种方法好像可以避免再次重新赋值
// 这里不是要加一个数据进去,直接给所有可释放区域一个标记就好了
}
},
end:(item,monitor)=>{
const newUselessIndex=newCardList.findIndex((v)=>{
if(item){
return v.category===item.name
}
})
const oldUseLessIndex=oldCardList.findIndex((v)=>{
if(item){
return v.category===item.name
}
})
const result=monitor.getDropResult()
// 拖拽放入的父容器的名字和行为
// 拖拽结束,判断是否将拖拽元素放入了目标接收组
// 如果是,用传入的元素代替占位元素
// 如果否,将占位元素删除
if(item&&result){
// item就是上面的item
// result 主要有以下参数
// dropeffect:move
// name:
// 这个name是容器的drop方法返回的name
// 如果在 drop target 的 drop 方法中返回了一个对象,在这里可以通过 monitor.getDropResult() 获取到返回结果
// console.log(`dropped ${item.name} into ${result.name}`)
}
if(monitor.didDrop()&&result){
// 拖放的时候检查是否有之前的
// if(newCardList.find((item)=>item.name===monitor.getItem().name)){
// }
newCardList.push(oldCardList[oldUseLessIndex])
//为新的数组添加拖拽过去的元素
oldCardList.splice(oldUseLessIndex,1)
}
changeOldCardList(oldCardList)
},
collect:(monitor)=>{
return {
isDragging:monitor.isDragging()
}
}
})
const opacity=isDragging?0.4:1
const backgroundColor=bg
return (
<div style={{...style,opacity,backgroundColor}} ref={drager}>{category}</div>
)
}
export default Box;
import React, { CSSProperties } from 'react';
import { useDrop, DropTargetMonitor } from 'react-dnd';
const style: CSSProperties = {
width: 100,
height: 100,
margin: '10px auto',
lineHeight: '60px',
border: '1px dashed black'
}
interface DustbinOptions{
accept:string
// 可以接受的传入数据
allowedDropEffect?:string,
// 接受拖拽的类型,移动,复制,或者任何类型
name:string,
}
// 这里必须这样写...
const Dustbin = ({accept,allowedDropEffect}:DustbinOptions) => {
// 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
const [collectProps, droper] = useDrop({
// accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
accept: accept,
// collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
collect: (minoter: DropTargetMonitor) => ({
isOver: minoter.isOver(),
canDrop:minoter.canDrop(),
}),
hover:(e)=>{
// console.log('有东西上来了')
},
drop:(e,ac)=>{
return {
name:"Dustbin",
allowedDropEffect,
}
}
})
// console.log('drop props',collectProps)
const isActive=collectProps.canDrop&&collectProps.isOver
const bg = isActive ? 'deeppink' : 'white';
const content = isActive ? '快松开,放到碗里来' : `将${accept}组件拖动到这里`
return (
// 将 droper 赋值给对应元素的 ref
<div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
)
}
export default Dustbin;
import React, { useState } from 'react';
import Box, { ListData } from './box';
import Dustbin from './Dustbin'
let arrlist=[
{ id: 1, category: 'Apple-1', bg: 'red',type:"box1"},
{ id: 2, category: 'Banana-2', bg: 'yellow',type:"box2"},
{ id: 3, category: 'Orange-1', bg: 'orange',type:"box1"},
{ id: 4, category: 'Grape-1', bg: 'purple',type:"box1"},
{ id: 5, category: 'Watermelon-2', bg: 'green',type:"box2"},
{ id: 6, category: 'Peach-1', bg: 'pink',type:"box1"},
]
const Container=()=>{
const [newCardList,setNewCardList]=useState<ListData[]>([])
const changeNewCardList=(list:ListData[])=>{
setNewCardList([...list])
}
const [oldCardList,setOldCardList]=useState<ListData[]>(arrlist)
const changeOldCardList=(list:ListData[])=>{
setOldCardList([...list])
// 这样就能保证每次都是新的值,有点秒
}
return (
<div>
{
arrlist.map((item)=><Box
{...item}
newCardList={newCardList}
oldCardList={arrlist}
key={item.id}
type={item.type}
changeNewCardList={changeNewCardList}
changeOldCardList={changeOldCardList}
/>)
// 还能这样传入参数吗?直接一个括号把所有的一股脑塞进去,好像后面的style也有这样的用法,其实就是解构对象然后传进去
}
{
newCardList.map((e)=>{
return <div key={e.id}>{e.category}</div>
}
)
}
<Dustbin accept="box1" allowedDropEffect="move" name="b1"></Dustbin>
<Dustbin accept="box2" allowedDropEffect="copy" name="b2"></Dustbin>
</div>
)
}
export default Container
他这里完全不能实现改变拖拽物体的鼠标样式
1 .没有一个接口可以在拖拽的时候移动被拖拽的物体,实际上移动的仅仅是被拖拽物体的快照。
2 .如果想要不出现手只能把整个界面变成可释放拖拽区域,这样在拖拽的时候就不会出现那个手了。这一点react-beautiful-dnd就实现的很好