极简主义拖拽排序dnd-kit(js版)

346 阅读2分钟

市面排序方案比较:

产品特性react-dndreact-beautiful-dnddnd-kit
兼容性触摸和 IE10+最新稳定版本的浏览器指针、鼠标、触摸、键盘
包体积900KB1.39 MB1MB
代码复杂度复杂较复杂较复杂

一口气吃不成胖子,我们选择相对简单的dnd-kit来上手

安装dnd-kit:

安装目录安装命令一句话描述用途
核心npm install @dnd-kit/core 实现基本的拖拽
扩展npm install @dnd-kit/sortable优化排序
工具npm install @dnd-kit/utilities实现拖拽视觉效果

核心步骤: 1.编写父组件: 1.1明确可拖拽区域,在可拖拽区域外层包裹DndContext和SortableContext: image.png 1.2 配置DndContext组件的属性onDragEnd(拖拽结束事件的回调函数,必传),在回调中完成拖拽的交换逻辑

image.png 2.编写子组件: 这里比较简单,从父组件传进来的props里取出id交给useSortable,给子组件最外层div带上ref、attributes、style属性即可,需要注意的是{...listeners}放在哪个元素上,哪个元素就是拖拽的抓手 image.png

代码贴在下面: 父组件代码:

import { DATA } from "../../const/data.js";
import SortItem from "../SortItem/index.jsx";
import { DndContext } from "@dnd-kit/core";
import {
  SortableContext,
  verticalListSortingStrategy,
  arrayMove,
} from "@dnd-kit/sortable";
import "./index.css";
import { useState } from "react";

const SortList = () => {
  const [list, setList] = useState(DATA);

  const handleDragEnd = (dragItem) => {
    const { active, over } = dragItem;
    if (!active || !over) return; // 处理边界情况
    const activeIndex = list.findIndex((item) => item.id === active.id);
    const overIndex = list.findIndex((item) => item.id === over?.id);
    const moveList = [...list];
    if (activeIndex !== overIndex) {
      const newList = arrayMove(moveList, activeIndex, overIndex);
      setList(newList);
    }
  };
  return (
    <DndContext onDragEnd={handleDragEnd}>
      <SortableContext
        items={list.map((item) => item.id)}
        strategy={verticalListSortingStrategy}
      >
        {/* 可拖拽区域 */}
        <div className="list-nav">
          {list.map((item) => (
            <SortItem key={item.id} id={item.id} content={item.content} />
          ))}
        </div>
      </SortableContext>
    </DndContext>
  );
};
export default SortList;


父组件的css:

.list-nav {
  display: flex;
  flex-direction: column;
  width: 200px;
  gap: 10px;
}

子组件代码:

import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import drag from "../../assets/drag.svg";
import "./index.css";

const SortItem = (props) => {
  const { id, content } = props;
  const { setNodeRef, attributes, listeners, transform, transition } =
    useSortable({
      id,
    });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  return (
    <div ref={setNodeRef} {...attributes} style={style} className="list-item">
      <span>{content}</span>
      <img src={drag} {...listeners} />
    </div>
  );
};

export default SortItem;

子组件css:

.list-item {
  position: relative;
  border-radius: 8px;
  width: 100%;
  box-shadow: 0 1px 2px 0 rgba(65, 218, 42, 0.05);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 40px;
  color: blanchedalmond;
  height: 40px;
  background-color: rgba(33, 140, 124, 0.6);
}

.list-item img {
  position: absolute;
  right: 8px;
  width: 30px;
  height: 30px;
}