安装
官方文档:docs.dndkit.com/presets/sor... > 使用 @dnd-kit 只需要安装他的核心库@dnd-kit/core 就够了 >
npm install @dnd-kit/core
或者yarn add @dnd-kit/core
@dnd-kit/core 有大多数用户在构建拖放体验时最需要的主要功能@dnd-kit/modifiers 动态修改坐标
@dnd-kit/sortable 排序专用
使用
1.需要引入 DndContext 组件,他类似 react 的 Context 向下传递状态,他必须在拖拽组件的最外层
javascript
import React from "react";
import { DndContext } from "@dnd-kit/core";
import { Draggable } from "./Draggable";
import { Droppable } from "./Droppable";
function App() {
return (
<DndContext>
<Draggable />
<Droppable />
</DndContext>
);
}
2.使用 useDroppable 创建一个可以掉落的区域
使用 ref 传递给您希望成为可掉落的 DOM 元素 传入唯一的 id
javascript
import React from "react";
import { useDroppable } from "@dnd-kit/core";
function Droppable(props) {
const { isOver, setNodeRef } = useDroppable({
id: "droppable",
});
const style = {
color: isOver ? "green" : undefined,
};
return (
<div ref={setNodeRef} style={style}>
{props.children}
</div>
);
}
3.使用 useDraggable 创建一个可以拖动的元素
使用 ref 传递给可拖拽的 DOM 元素 传入唯一的 id
javascript
import React from 'react';
import {useDraggable} from '@dnd-kit/core';
function Draggable(props) {
const {attributes, listeners, setNodeRef, transform} = useDraggable({
id: 'draggable',
});
const style = transform ? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
} : undefined;
return (
<button ref={setNodeRef} style={style} {...listeners} {...attributes}>
{props.children}
</button>
);
}
}
说明
DndContext
该 提供程序使用 React Context API 在可拖动和可放置的组件和钩子之间共享数据 可嵌套使用
Props
都是选填
- id:唯一标识
- autoScroll:控制自动滚动的行为。可以是一个布尔值,表示是否启用自动滚动,默认为 true;也可以是一个函数,用于自定义自动滚动的行为。
- onDragStart(e):拖动开始时触发的回调函数。
- e.active:拖动的元素信息{data,id,rect}
- onDragMove(e):拖动过程中触发的回调函数
- onDragOver(e):拖动过程中鼠标悬停在某个区域时触发的回调函数
- onDragEnd(e):拖动结束时触发的回调函数
- e.active:拖动的元素信息
- e.over:drop(接收拖拽元素)的元素信息
- onDragCancel(e):拖动被取消时触发的回调函数,按下 esc 键会取消
- collisionDetection:指定碰撞检测算法的函数,默认为基于边界框的 AABB (Axis-Aligned Bounding Box) 碰撞检测算法。
- sensors:详情见后面的传感器。他一个数组,包含要使用的传感器。传感器用于捕捉拖放操作的各种事件。默认情况下,传感器包括 MouseSensor、TouchSensor 和 KeyboardSensor。你可以根据需要选择性地启用或禁用传感器。
- modifiers:一个数组,包含要使用的修饰器。修饰器用于修改拖放行为的方式。默认情况下,修饰器包括 RestrictToVerticalAxisModifier 和 RestrictToHorizontalAxisModifier。你可以根据需要选择性地启用或禁用修饰器。
useDroppable
需要将 useDroppable 钩子返回的 setNodeRef 函数传递给 DOM 元素 要设置多个可放置目标,只需根据需要多次使用 useDroppable 钩子即可 你可以使用这些返回值来根据拖动项是否放置在可放置区域上方来修改组件的样式或触发其他逻辑。例如,你可以根据 isOver 来显示一个放置指示器,或者根据 isOverCurrent 来修改放置区域的背景色。 需要注意的是,useDroppable 只能在可放置区域的直接子元素上使用。如果你想将其应用于更深层级的元素上,可以使用 useDroppableOverlay 钩子。
参数,是一个对象
- id(string,必填):唯一标识可放置区域的 ID。
- data(任意类型):可选的自定义数据,可以在放置事件中使用。
- disabled(boolean):可选,用于禁用可放置区域。
返回值,返回一个对象
- setNodeRef:用于绑定可掉落的元素的 ref
- active:当前拖动元素的信息
- node:当前可放置区域的 DOM 节点
- rect:返回的是表示当前 Droppable 组件边界框的 DOMRect 对象
- top:边界框的上边缘相对于视口的 y 坐标
- right:边界框的右边缘相对于视口的 x 坐标
- bottom:边界框的下边缘相对于视口的 y 坐标
- left:边界框的左边缘相对于视口的 x 坐标
- width: 矩形的宽度。
- height: 矩形的高度
- isOver:是否有拖拽元素滑动在该元素上,和 css 中的 hover 一样
- over:drop(接收拖拽元素)的元素信息
useDraggable
需要将 useDroppable 钩子返回的 setNodeRef 函数传递给 DOM 元素 需要用 style 里的 transform 属性来控制元素的位置 需要把返回值里的 listeners 传递给 DOM 元素 例如
<button ref={setNodeRef} style={style} {...listeners} {...attributes}>
建议将返回值 attributes 传递给 DOM 元素
参数,是一个对象
- id:必填,唯一标识符
- disabled:是否禁用
- data:该元素的自定义数据
- attributes:
返回值,返回一个对象
-
setNodeRef:用于绑定可掉落的元素的 ref
-
node:当前拖动的 dom 元素
-
isOver:是否有拖拽元素滑动在该元素上,和 css 中的 hover 一样
-
over:drop(接收拖拽元素)的元素信息
-
active:当前拖动元素的信息
- id:当前元素 id
- data:当前元素参数里的 data
- rect:
-
isDragging:是否正在拖动
-
activeNodeRect:当前拖动的 dom 元素
-
attributes:建议传递给 DOM 元素,详情见docs.dndkit.com/api-documen...
-
listeners:拖动手柄,需要传递给 DOM 元素才能拖动
-
transform:拖动的鼠标移动信息
- x:x 坐标移动距离
- y:y 坐标移动距离
- scaleX:x 轴缩放
- scaleY:y 轴缩放
- css 帮助程序
javascriptCSS.Translate.toString(transform) === `translate3d(${translate.x}, ${translate.y}, 0)`; //使用 import { CSS } from "@dnd-kit/utilities"; const style = { transform: CSS.Translate.toString(transform), };
DragOverlay
他是一个组件,当开始拖动时,他会显示到拖动的组件上面,拖动结束就会隐藏 即使不加 style 也可以拖动,useDraggable 必须加 style 才会移动 该叠加从正常文档流中删除并相对于视区定位
javascript
<DragOverlay>
<div>234234</div>
</DragOverlay>
Sensors (传感器)
传感器是一种抽象概念,用于检测不同的输入方法,以便启动拖动操作、响应移动以及结束或取消操作。 传感器负责监视用户的手势、拖动元素和目标元素之间的交互,并将交互事件传递给 DndContext 组件
javascript
import { DndContext, useSensor } from "@dnd-kit/core";
function MyComponent() {
const sensor = useSensor(/* 传感器配置参数 */);
return <DndContext sensors={[sensor]}>{/* 其他组件 */}</DndContext>;
}
内置传感器包括
- Pointer(指针)(Pointer 是一个广义的术语,用于表示用户输入设备,可以是鼠标、触摸屏、手写笔等)
- 鼠标
- 触摸
- 键盘 传感器的生命周期如下
- 检测到激活器事件,如果该事件合格,则初始化传感器类。
- 传感器在初始化时手动将新侦听器连接到输入方法。
- 一旦满足约束,传感器就会分派拖动启动事件。
- 传感器发送拖动移动事件以响应输入。
- 传感器发送拖动结束或拖动取消事件。
- 传感器被拆除并清理手动连接的事件侦听器。
使用传感器
默认情况下, DndContext 使用指针和键盘传感器 如果要改用其他传感器(例如鼠标和触摸传感器),请使用要使用的选项 useSensor 分别初始化这些传感器
javascript
import {
DndContext,
KeyboardSensor,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
function App() {
const sensors = useSensors(
useSensor(MouseSensor),
useSensor(TouchSensor),
useSensor(KeyboardSensor),
);
return (
<DndContext sensors={sensors}>
{/* ... */}
</DndContext>
)
}
Pointer(指针)
Modifiers(修饰器)
使用修改器可以动态修改传感器检测到的移动坐标
安装
npm install @dnd-kit/modifiers
使用
javascript
import { DndContext, DragOverlay } from "@dnd-kit";
import {
restrictToVerticalAxis,
restrictToWindowEdges,
} from "@dnd-kit/modifiers";
function App() {
return (
<DndContext modifiers={[restrictToVerticalAxis]}>
{/* ... */}
<DragOverlay modifiers={[restrictToWindowEdges]}>{/* ... */}</DragOverlay>
</DndContext>
);
}
内置的修饰符
- restrictToHorizontalAxis:将移动限制在水平轴上。
- restrictToVerticalAxis:将移动限制为仅垂直轴。
- restrictToWindowEdges:将移动限制在窗口的边缘。此修改器可用于防止 DragOverlay 移动到窗口边界之外。
- restrictToParentElement:限制移动到可拖动项目的父元素。
- restrictToFirstScrollableAncestor:将移动限制为拾取的可拖动项目的第一个可滚动祖先。
网格对齐
使用 createSnapModifier 创建网格,参数是网格大小
javascript
import {createSnapModifier} from '@dnd-kit/modifiers';
const gridSize = 20; // pixels
const snapToGridModifier = createSnapModifier(gridSize);
<DndContext onDragStart={onDragStart} onDragEnd={onDragEnd} modifiers={[snapToGridModifier]}>
排序专用
安装
yarn add @dnd-kit/sortable
使用
需要把useSortable的返回值attributes和listeners传入组件
javascript
import React, { useState } from "react";
import { DndContext, useDroppable, useDraggable ,DragOverlay} from "@dnd-kit/core";
import {SortableContext,useSortable} from '@dnd-kit/sortable';
import { CSS } from "@dnd-kit/utilities";
export default function App() {
const [items,setItems] = useState<number[]>([1,2,3,4])
const onDragEnd =(e)=>{
console.log('end',e);
}
return (
<DndContext onDragEnd={onDragEnd}>
<SortableContext items={items}>
</SortableContext>
</DndContext>
);
}
function SortableItem({id}) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({id});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
{id}0000000
</div>
);
}
SortableContext
和 DndContext 一样用于共享数据 为了使 SortableContext 组件正常运行,请确保它是 DndContext 提供程序的后代 可以在同一父DndContext中嵌套多个SortableContext组件
javascript
import React, { useState } from "react";
import { DndContext } from "@dnd-kit/core";
import { SortableContext } from "@dnd-kit/sortable";
export default function App() {
const [items] = useState([1, 2, 3]);
return (
<DndContext>
<SortableContext items={items}>{/* ... */}</SortableContext>
</DndContext>
);
}
props
- disabled:禁用
- items:必填每个可排序项关联的唯一标识符的排序数组,必须按照渲染项目的相同顺序进行排序
- strategy:排序策略,内置策略包
- rectSortingStrategy :这是默认值,适用于大多数用例。此策略不支持虚拟化列表。
- verticalListSortingStrategy :此策略针对垂直列表进行了优化,并支持虚拟化列表。
- horizontalListSortingStrategy :此策略针对水平列表进行了优化,并支持虚拟化列表。
- rectSwappingStrategy :使用此策略实现可交换功能
import {verticalListSortingStrategy} from '@dnd-kit/sortable';
<SortableContext items={items} strategy={verticalListSortingStrategy}>
- id:唯一标识
useSortable
参数
传入一个对象
- id:必填,唯一标识符
- disabled:是否禁用
- transition:要完全禁用过渡,请将 transition 参数设置为 null
返回值
返回一个对象
- active:
- listeners:父 DndContext 提供程序上定义的每个传感器的激活器事件处理程序
- attributes:
- transform:
- setNodeRef
arrayMove
用于计算排序位置
javascript
import React, { useState } from "react";
import { DndContext} from "@dnd-kit/core";
import {SortableContext,useSortable,arrayMove} from '@dnd-kit/sortable';
import { CSS } from "@dnd-kit/utilities";
export default function App() {
const [items,setItems] = useState<number[]>([1,2,3,4])
function handleDragEnd(event) {
const {active, over} = event;
if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.indexOf(active.id);
const newIndex = items.indexOf(over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
return (
<DndContext onDragEnd={handleDragEnd}>
<SortableContext items={items}>
{items.map(item=><SortableItem id={item}/>)}
</SortableContext>
</DndContext>
);
}