教程
使用
DndProvider 的本质是一个由 React.createContext 创建一个上下文的容器(组件),用于控制拖拽的行为,数据的共享,类似于 react-redux 的 Provider。
- react-dnd-html5-backend : 用于控制 html5 事件的 backend
import { HTML5Backend } from "react-dnd-html5-backend";
import { DndProvider } from "react-dnd";
<DndProvider backend={HTML5Backend}>
<TutorialApp />
</DndProvider>;
useDrag
使用 useDrag 可创建一个拖动源,他会 ref 用于绑定拖动源
参数
- 第一个参数,是一个函数返回一个对象,是用于描述了 drag 的配置信息和常用属性
- type:必填。这必须是字符串或 symbol。只有为相同类型注册的放置目标才会对此项目做出反应
- item:必填。对象或函数,拖动源的信息。
- 当是对象时,这是放置目标可获得的有关拖动源的唯一信息
- 当这是一个函数时,它会在拖动操作开始时触发,并返回一个表示拖动操作的对象(请参见第一个项目符号)。如果返回 null,则拖动操作被取消。
- end(item, monitor):选填。当拖动停止时,end 被调用,monitor 是当前拖动源实例
- monitor.didDrop()可以判断当前拖动源是否掉落在了放置目标里,放在了空白的地方就会返回 false
- canDrag(monitor):选填。返回一个布尔值,表示是否可以拖拽
- isDragging(monitor):选填。返回一个布尔值,表示是否在拖拽中。可以覆盖 Monitor 对象中的 isDragging 方法,从而决定 collect 里返回的 isDragging
- collect(monitor):选填.他返回一个对象,就是 useDrag,返回结果解构的第一个值
- 用于记忆的依赖数组。这就像内置的 useMemoReact hook 一样。默认值为空数组,所以如果有依赖一定要加到数组里
返回值
返回一个数组可以解构 3 个参数
- 从 collect 函数收集的属性的对象
- 当前拖动的 ref
- 拖动之后留在原地的 ref
解释
-
item:拖动源的 item 参数,这个 item 只是临时的可以修改他,不会影响原来的数据,重新拖动之后就会变了
-
monitor 实例:他是一个对象有以下方法
如果被drop组件包裹了,计算鼠标偏移量的好像都获取不到了,可以在drop中return出来,然后在getDropResult方法里获取
- canDrag():返回一个布尔值。表示是否可以拖动,如果第一个参数里的 canDrag() 返回 true 或未定义,则返回 true。
- isDragging():返回一个布尔值。表示是否正在拖动,或者 isDragging() 已定义并返回 true,则返回 true。
- getItemType():返回字符串。获取拖动源的 type 属性,就是第一个参数里的 type
- getItem():返回一个对象。获取拖动源的 item 属性,就是第一个参数里的 item
- didDrop():返回一个布尔值。判断当前拖动源是否掉落在了放置目标里,放在了空白的地方就会返回 false
- getDropResult():可以获取 useDrop 的 drop 方法里 return 的对象
下面的好像都是计算鼠标偏移量的
- getInitialClientOffset():返回{ x, y }当前拖动操作开始时鼠标的距离左边和顶部的距离
- getInitialSourceClientOffset():返回{ x, y }操作开始时拖动源组件的左上角距离左边和顶部的距离
- getClientOffset():返回{ x, y }返回鼠标最后的位置
- getDifferenceFromInitialOffset():返回{ x, y }最后记录的鼠标位置与拖动操作开始时的鼠标值之间的差值
- getSourceClientOffset(): x, y }返回拖动源组件的左上角距离左边和顶部的距离
useDrop
参数
- 第一个参数,是一个函数返回一个对象,是用于描述了 drop 的配置信息和常用属性
- accept:必填。这必须是字符串或 symbol。只有和 useDrag 的 type 相同才行,要不然什么都不会触发
- drop(item, monitor):选填:当兼容项目掉落到目标上时调用。如果返回一个对象,可以在拖动源的 end 方法里用 monitor.getDropResult()获取到
- hover(item, monitor):当项目悬停在组件上时调用
- canDrop(item, monitor):返回一个布尔值,表示拖拽物是否可以放置
- collect(monitor):选填.他返回一个对象,就是 useDrop,返回结果解构的第一个值
- 用于记忆的依赖数组。这就像内置的 useMemoReact hook 一样。默认值为空数组,所以如果有依赖一定要加到数组里
返回值
返回一个数组可以解构 2 个参数
- 从 collect 函数收集的属性的对象
- 当前盒子的 ref
解释
-
item:拖动源的 item 参数,这个 item 只是临时的可以修改他,不会影响原来的数据,重新拖动之后就会变了
-
monitor 实例:他是一个对象有以下方法
- canDrop():返回一个布尔值。表示是否可以掉落,如果第一个参数里的 canDrop() 返回 true 或未定义,则返回 true。
- isOver({shallow?: boolean;}):返回一个布尔值。表示指针当前是否悬停在所有者上方。传递 {shallow: true} 来严格检查是否只有所有者被悬停,而不是 到嵌套目标
- getItem():获取拖拽源的 item
- getDropResult():获取第一个参数里 drop 方法返回的对象
- didDrop():返回一个布尔值
下面的好像都是计算鼠标偏移量的
- getInitialClientOffset():返回{ x, y }当前拖动操作开始时鼠标的距离左边和顶部的距离
- getInitialSourceClientOffset():返回{ x, y }操作开始时拖动源组件的左上角距离左边和顶部的距离
- getClientOffset():返回{ x, y }返回鼠标最后的位置
- getDifferenceFromInitialOffset():返回{ x, y }最后记录的鼠标位置与拖动操作开始时的鼠标值之间的差值
- getSourceClientOffset(): x, y }返回拖动源组件的左上角距离左边和顶部的距离
//1.引用useDrag
import { useDrag } from 'react-dnd'
function DraggableComponent(props) {
const [collected, drag, dragPreview] = useDrag(() => ({
type:'',
item: { id },
collect: (monitor: any) => ({
//当传入isDragging方法时,monitor.isDragging()方法指代传入的方法
isDragging: monitor.isDragging(),
}),
}))
return collected.isDragging ? (
<div ref={dragPreview} />
) : (
<div ref={drag} {...collected}>
...
</div>
)
}
function MyDropTarget(props) {
const {data,update} = useContext<any>(myConText);
const [collectedProps, drop] = useDrop(() => ({
drop:(item:any,monitor)=>{
update({ids:[...data?.ids,item?.id]})
},
collect: (monitor) => {
return {
isDragging: monitor.isDragging(),
getDropResult:monitor.getDropResult(),
getDifferenceFromInitialOffset:monitor.getDifferenceFromInitialOffset()
}}
accept:'BOX'
}),[data])
return <div ref={drop} className='drops'>
{data?.ids?.map((item,index)=>{
return <Box key={index} id={item}/>
})}
</div>
}
自由拖动案例
export default function Text() {
const [dropList, setDropList] = useState([1, 2, 3, 4, 5]);
return (
<DndProvider backend={HTML5Backend}>
<MyDrop>
<>
<Word type="box" text={"myDrag"} id={1} />
</>
</MyDrop>
</DndProvider>
);
}
const MyDrop = (props) => {
const [, drop] = useDrop(() => ({
accept: "box",
drop(_item: any, monitor: any) {
const delta = monitor.getDifferenceFromInitialOffset();
const left = Math.round(delta.x);
const top = Math.round(delta.y);
return { top, left };
},
}));
return (
<div ref={drop} style={{ width: "100%", height: "100%" }}>
{props.children}
</div>
);
};
const Word = ({ type, text, id, ...props }: any) => {
const [offsetX, setOffsetX] = useState(0);
const [offsetY, setOffsetY] = useState(0);
const [{ isDragging }, drag]: any = useDrag(() => ({
type,
item: { id, type },
end(item, monitor) {
let top = 0,
left = 0;
if (monitor.didDrop()) {
const dropRes = monitor.getDropResult() as any;
if (dropRes) {
top = dropRes.top;
left = dropRes.left;
}
setOffsetX((offsetX) => offsetX + left);
setOffsetY((offsetY) => offsetY + top);
} else {
setOffsetX(0);
setOffsetY(0);
}
},
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
}));
return (
<div
className="word_drag"
id={id}
ref={drag}
style={{
opacity: isDragging ? 0 : 1,
top: `${offsetY}px`,
left: `${offsetX}px`,
}}
>
{text}
</div>
);
};