基础概念
DndContext
用于包装拖拽根组件,Draggable
和Droppable
都需要包裹在DndContext
内Droppable
用于包装接收拖拽元素的组件,使组件能够放置Draggable
用于包装需要拖动的组件,使组件能够被拖拽SortableContext
创建一个拖放排序的容器,其中的子元素可以被拖动并重新排序Sensors
用于检测不同的输入方法,以启动拖动操作、响应移动以及结束或取消操作,内置传感器:指针、鼠标、触摸、键盘
安装依赖
@dnd-kit/core: 6.0.8, @dnd-kit/sortable: 7.0.2, @dnd-kit/utilities: 3.2.1,
useSensors
是@dnd-kit
库中的一个函数,用于配置和管理拖拽操作的传感器。传感器是用于检测用户交互的输入设备或方式的模块。useSensor(PointerSensor)
行创建了一个名为PointerSensor
的传感器实例,并将其添加到sensors
数组中。PointerSensor
用于捕获指针设备(如鼠标或触摸屏)的交互,允许用户通过拖拽来操作元素。useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
行创建了一个名为KeyboardSensor
的传感器实例,并将其添加到sensors
数组中。这里还提供了一个选项对象,指定了coordinateGetter
函数,用于确定键盘输入的坐标。KeyboardSensor
允许用户使用键盘来操作元素,通常用于键盘控制的排序。
js
import React, { FC, useState } from "react";
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragEndEvent,
} from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import Item from "./Item";
type ComponentType = {
fe_id: string;
title: string;
};
const Container: FC = () => {
const [items, setItems] = useState<ComponentType[]>([
{ fe_id: "c1", title: "组件1" },
{ fe_id: "c2", title: "组件2" },
{ fe_id: "c3", title: "组件3" },
]);
// 配置和管理拖拽操作的传感器
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
const itemsWithId = items.map((c) => {
return {
...c,
id: c.fe_id,
};
});
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
>
<SortableContext
items={itemsWithId}
strategy={verticalListSortingStrategy}
>
{itemsWithId.map((c) => (
<Item key={c.id} id={c.id} title={c.title} />
))}
</SortableContext>
</DndContext>
);
};
export default Container;
设置 onDragEnd
函数。
- 从
event
对象中解构出active
和over
属性。active
代表拖动操作的起点元素,over
代表拖动操作的结束点元素。 - 检查起点元素和结束点元素的
id
是否不相同。 - 使用
arrayMove
函数将active
元素从oldIndex
移动到newIndex
,实现元素的重新排序。
js
function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
if (over == null) return;
if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.findIndex((c) => c.fe_id === active.id);
const newIndex = items.findIndex((c) => c.fe_id === over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
Item.tsx
attributes
: 这是一个包含属性的对象,通常应用于拖拽目标元素,以便处理拖拽的交互。这些属性包括样式属性、拖拽相关的属性等,用于实现拖拽时的视觉效果。listeners
: 这是一个包含事件监听器的对象,用于监听拖拽过程中的事件,例如拖拽开始、拖拽结束等。这些事件监听器应该绑定到拖拽目标元素,以便捕获用户的拖拽操作。setNodeRef
: 这是一个回调函数,用于设置拖拽目标元素的引用。通常,你需要将这个回调函数传递给要进行拖拽的元素,以确保能够正确地识别和操作拖拽目标。transform
: 这是一个用于处理拖拽元素的变换效果的 CSS 属性。可以使用它来应用平移(translation)、缩放(scale)等变换,以使拖拽效果看起来更加自然。transition
: 这是一个用于定义拖拽元素的过渡效果的 CSS 属性。可以使用它来创建平滑的过渡动画,使拖拽操作更加流畅。
js
import React, { FC } from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
type PropsType = {
id: string;
title: string;
};
const Item: FC<PropsType> = (props: PropsType) => {
const { id, title } = props;
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
border: "1px solid #ccc",
margin: "10px 0",
background: "#f1f1f1",
};
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
Item {title}
</div>
);
};
export default Item;