dnd kit列表拖拽使用
1、使用前提:
下载以下5个依赖:
@dnd-kit/core
@dnd-kit/accessibility
@dnd-kit/sortable
@dnd-kit/modifiers
@dnd-kit/utilities
2、使用说明
2.1 标签说明
1、DndContext:dnd kit的容器,想要使用拖拽操作,必须要有这个,并且滑动列表的拖拽事件都在这里写(onDragMove,onDragEnd等等)
2、SortableContext:滑动列表的容器,相当于ul,内部的列表配置好了可以拖拽(items为必填项,表示哪些列表在我这个容器中滑动,items为唯一标识的数组)
2.2 useSortable使用说明
1、作用:将唯一标志变为一个列表的内部项
2、使用方式:useSortable({id:XXX})
3、返回值说明:
1、setNodeRef:关联dom节点,使其成为一个可拖拽的项
2、listeners:包含onKeyDown,onPointerDown方法,主要让节点可以进行拖拽
3、transform:该节点被拖动时候的移动变化值
4、transition:过渡效果
5、isDragging:节点是否在拖拽
3、具体用法
3.1 单列表滑动
3.1.1 可拖动的列表
仅仅只能拖动,不能更新列表
import { DndContext } from "@dnd-kit/core";
import { SortableContext, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
// 容器组件
export default function SingleTest() {
const items = ["A","B","C"]
return (
<DndContext>
<SortableContext items={items}>
{
items.map(val=>(<Item id={val}/>))
}
</SortableContext>
</DndContext>
)
}
// 拖拽项组件
function Item(props:any) {
const { id } = props
const {setNodeRef,listeners,transform,transition } = useSortable({id})
const styles = {
transform:CSS.Transform.toString(transform),
border: "1px solid red",
marginTop: "10px"
}
return (
<div ref={ setNodeRef } {...listeners} style={styles}>{id}</div>
)
}
3.1.2 拖动后并更新列表
import { DndContext } from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useState } from "react";
// 容器组件
export default function SingleTest() {
const [items,setItems] = useState(["A","B","C"])
// 拖拽结束后的操作
function dragEndEvent(props:any) {
const { active,over } = props
const activeIndex = items.indexOf(active.id)
const overIndex = items.indexOf(over.id)
setItems(items=>{
return arrayMove(items,activeIndex,overIndex)
})
}
return (
<DndContext onDragEnd={dragEndEvent}>
<SortableContext items={items}>
{
items.map(val=>(<Item id={val}/>))
}
</SortableContext>
</DndContext>
)
}
// 拖拽项组件
function Item(props:any) {
const { id } = props
const {setNodeRef,listeners,transform,transition } = useSortable({id})
const styles = {
transform:CSS.Transform.toString(transform),
border: "1px solid red",
marginTop: "10px"
}
return (
<div ref={ setNodeRef } {...listeners} style={styles}>{id}</div>
)
}
3.2 多列表滑动
3.2.1 多个可拖拽的列表
import { DndContext } from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useState } from "react";
export default function SingleTest() {
const [items,setItems] = useState({
"A":["A1","A2","A3"],
"B":["B1","B2","B3"]
})
// 找到容器
function findContaniner(id:string) {
if (id in items) {
return id;
}
return Object.keys(items).find((key) =>
items[key].find((item:string)=>item === id)
);
}
// 设置从一个容器到另一个容器时候的变化
function dragMoveEvent(props:any) {
const { active,over } = props
const overId = over?.id
if(!overId) return
const activeContainer = findContaniner(active?.id) || ""
const overContainer = findContaniner(over?.id) || ""
const dragItem = active.id
if (!overContainer || !activeContainer) {
return;
}
// 将activeContainer里删除拖拽元素,在overContainer中添加拖拽元素
if(activeContainer !== overContainer) {
const overIndex = items[overContainer].indexOf(over.id)
const newIndex = overIndex >= 0 ? overIndex : items[overContainer].length + 1;
const data = {
...items,
[activeContainer]: items[activeContainer].filter((item:string)=>{
return item!==active.id
}),
[overContainer]:[
...items[overContainer].slice(0,newIndex),
dragItem,
...items[overContainer].slice(newIndex,items[overContainer].length)
]
}
setItems(data)
}
}
// 设置移动结束后时候的改变
const dragEndFn = (props:any) => {
const { over,active } = props
const overId = over?.id;
const activeId = active?.id
const activeContainer = findContaniner(activeId) || "";
const overContainer = findContaniner(overId) || "";
const activeItems = items[activeContainer]
const overItems = items[overContainer]
if (!activeContainer) return;
if (!overId) return;
if (overContainer) {
const overIndex = overItems.findIndex((item:string)=> item === overId);
const activeIndex = activeItems.findIndex((item:string)=> item === activeId);
if (activeIndex !== overIndex) {
setItems((items) => ({
...items,
[overContainer]: arrayMove(
overItems,
activeIndex,
overIndex
),
}));
}
}
}
return (
<DndContext onDragMove={dragMoveEvent} onDragEnd={dragEndFn}>
<SortableContext items={items['A']}>
{
items['A'].map(val=>(<Item id={val} key={val}/>))
}
</SortableContext>
<div>--------------------------------------------</div>
<SortableContext items={items['B']}>
{
items['B'].map(val=>(<Item id={val} key={val}/>))
}
</SortableContext>
</DndContext>
)
}
function Item(props:any) {
const { id } = props
const {setNodeRef,listeners,transform,transition } = useSortable({id})
const styles = {
transform:CSS.Transform.toString(transform),
border: "1px solid red",
marginTop: "10px"
}
return (
<div ref={ setNodeRef } {...listeners} style={styles}>{id}</div>
)
}
===注意:此处有坑(系统自带的CSS方法在多列表时会出现一些问题,会导致不流畅)===
3.2.2 解决系统自定义CSS方法带来的问题
// 只需要重写单个拖拽项的styles即可(弃用系统自带CSS方法)
function Item(props:any) {
const { id } = props
const {setNodeRef,listeners,transform,transition } = useSortable({id})
const styles = isDragging?{
transform: `translate3d(0px, ${transform?.y}px, 0) scaleX(1) scaleY(1)`,
border: "1px solid red",
marginTop: "10px"
}:undefined
return (
<div ref={ setNodeRef } {...listeners} style={styles}>{id}</div>
)
}
3.3 解决拖拽带来的点击事件失效
出现原因:
1、点击事件onClick和拖拽事件的onPointerDown有部分重叠,导致点击的时候系统无法准确的知道你是click还是pointerDown
解决办法:
1、添加sensor传感器,增加一个延迟,如下:
import { DndContext } from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useState } from "react";
export default function SingleTest() {
const [items,setItems] = useState({
"A":["A1","A2","A3"],
"B":["B1","B2","B3"]
})
// 设置从一个容器到另一个容器时候的变化
function dragMoveEvent(props:any) {}
// 设置移动结束后时候的改变
function dragEndFn(props:any){}
const sensors = useSensors(useSensor(PointerSensor,{
activationConstraint: {
delay: 100,
tolerance: 0,
}
}))
return (
<DndContext onDragMove={dragMoveEvent} onDragEnd={dragEndFn} sensors={sensors}>
<SortableContext items={items['A']}>
{
items['A'].map(val=>(<Item id={val} key={val}/>))
}
</SortableContext>
<div>------------------------------------------------------</div>
<SortableContext items={items['B']}>
{
items['B'].map(val=>(<Item id={val} key={val}/>))
}
</SortableContext>
</DndContext>
)
}
// 单个拖拽项
function Item(props:any) {
const { id } = props
const {setNodeRef,listeners,transform,transition } = useSortable({id})
const styles = {
transform:CSS.Transform.toString(transform),
border: "1px solid red",
marginTop: "10px"
}
return (
<div ref={ setNodeRef } {...listeners} style={styles}>{id}</div>
)
}