React实现列表拖拽排序

本文主要介绍一下React实现列表拖拽排序方法,具体样式如下图

首先,简单展示一下组件的数据结构

javascript 复制代码
const CodeSetting = props => {
    const {
        $t,                    // 国际化翻译函数
        vm,                    // 视图模型数据
        vm: {
            CodeSet: { 
                Enable = [],    // 启用的编码列表
                Disable = []    // 停用的编码列表
            }
        },
        getConfig,             // 获取配置的函数
        save,                  // 保存配置的函数
        vmChange               // 更新视图模型的函数
    } = props;
};

完整的数据结构示例

javascript 复制代码
const vm = {
    CodeSet: {
        Enable: [
            { Compression: "H.264" },
            { Compression: "H.265" },
            { Compression: "MPEG-4" }
        ],
        Disable: [
            { Compression: "AVC" },
            { Compression: "HEVC" }
        ]
    }
};

主要用到的代码如下,简单看后我将介绍拖拽方法

javascript 复制代码
{Enable.length ? (
                    <Card
                        title={`${$t('com.EnableCode')} (${Enable.length})`}
                        extra={
                            <Button
                                size='small'
                                className='clear-all-btn'
                                type='link'
                                onClick={clearAllEnabled}>
                                {$t('com.ClearAll')}
                            </Button>
                        }>
                        {Enable.map((item, index) => (
                            <div
                                key={index}
                                className='drag-item'
                                draggable
                                onDragStart={e => {
                                    handleDragStart(e, index);
                                }}
                                onDragEnd={handleDragEnd}
                                onDragOver={handleDragOver}
                                onDrop={e => handleDrop(e, index)}>
                                <div className='drag-handle'>⋮⋮</div>
                                <LabelText text={item.Compression} />
                                <div className='delete-btn-container'>
                                    <Icon
                                        component={remove}
                                        onClick={() => codeSetChange('remove', index)}
                                        style={{
                                            fontSize: '20px'
                                        }}
                                    />
                                </div>
                            </div>
                        ))}
                    </Card>

首先用到的组件是Card组件,title是card标题,extra是card后缀

之后遍历Enable数组,将拿到的每一个值渲染到card中

这个组件实现了 HTML5 原生拖拽 API 来实现编码列表的拖拽排序功能。主要使用了以下拖拽事件:

javascript 复制代码
onDragStart - 拖拽开始
onDragOver - 拖拽悬停
onDrop - 拖拽放置
onDragEnd - 拖拽结束

状态管理

javascript 复制代码
const [draggedIndex, setDraggedIndex] = useState(null); // 记录当前拖拽项的索引

拖拽事件处理函数

1 拖拽开始 (handleDragStart)

javascript 复制代码
const handleDragStart = (e, index) => {
    setDraggedIndex(index);                    // 记录拖拽项的索引
    e.dataTransfer.effectAllowed = 'move';    // 设置拖拽效果为移动
    e.currentTarget.classList.add('dragging'); // 添加拖拽样式
};

2 拖拽悬停 (handleDragOver)

javascript 复制代码
const handleDragOver = e => {
    e.preventDefault();                         // 阻止默认行为
    e.dataTransfer.dropEffect = 'move';        // 设置放置效果为移动
    
    // 清除所有拖拽项的悬停样式
    const dragItems = document.querySelectorAll('.drag-item');
    dragItems.forEach(item => {
        item.classList.remove('drag-over');
    });
    
    // 为当前悬停元素添加悬停样式
    e.currentTarget.classList.add('drag-over');
};

3 拖拽放置 (handleDrop)

javascript 复制代码
const handleDrop = (e, dropIndex) => {
    e.preventDefault();
    e.currentTarget.classList.remove('drag-over');

    if (draggedIndex === null || draggedIndex === dropIndex) {
        return;
    }

    // 重新排序 Enable 数组
    const enableList = [...Enable];
    const draggedItem = enableList[draggedIndex];

    // 移除拖拽项
    enableList.splice(draggedIndex, 1);

    // 在目标位置插入
    enableList.splice(dropIndex, 0, draggedItem);

    // 更新vm数据
    const newCodeSet = {
        ...vm.CodeSet,
        Enable: enableList
    };

    vmChange({ CodeSet: newCodeSet });
    setDraggedIndex(null);
};

4 拖拽结束 (handleDragEnd)

javascript 复制代码
const handleDragEnd = e => {
    setDraggedIndex(null);
    e.currentTarget.classList.remove('dragging');
    
    // 清除所有拖拽项的悬停样式
    const dragItems = document.querySelectorAll('.drag-item');
    dragItems.forEach(item => {
        item.classList.remove('drag-over');
    });
};

5.JSX 结构

javascript 复制代码
<div
    key={index}
    className='drag-item'
    draggable                                    // 设置为可拖拽
    onDragStart={e => handleDragStart(e, index)} // 拖拽开始
    onDragEnd={handleDragEnd}                    // 拖拽结束
    onDragOver={handleDragOver}                  // 拖拽悬停
    onDrop={e => handleDrop(e, index)}>          // 拖拽放置
    <div className='drag-handle'>⋮⋮</div>        // 拖拽手柄
    <LabelText text={item.Compression} />
    <div className='delete-btn-container'>
        {/* 删除按钮 */}
    </div>
</div>

6. 核心算法

  1. 拖拽排序的核心算法是数组重排序:
  2. 获取拖拽项:从原位置取出拖拽的元素
  3. 移除拖拽项:在原位置删除该元素
  4. 插入新位置:在目标位置插入该元素
  5. 更新状态:将新的数组顺序更新到组件状态

7. 样式处理

组件通过 CSS 类名来管理拖拽状态:

.dragging - 拖拽中的样式

.drag-over - 拖拽悬停的样式

.drag-item - 可拖拽项的基础样式

8.样式代码

javascript 复制代码
    // 拖拽项容器
    .drag-item {
        display: flex;
        align-items: center;
        cursor: grab;
        &:hover {
            background-color: #f5f5f5;
        }

        // 拖拽中状态
        &.dragging {
            background-color: #e6f7ff;
            opacity: 0.5;
            transform: scale(0.95);
        }
        // 拖拽悬停状态
        &.drag-over {
            background-color: #e6f7ff;
            border: 1px solid #91d5ff;
            border-radius: 4px;
        }
    }

    // 拖拽手柄
    .drag-handle {
        margin-right: 8px;
        color: #2f2e2e;
        font-size: 12px;
        user-select: none;
    }
相关推荐
IT_陈寒40 分钟前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen43 分钟前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
牧艺1 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
红尘散仙2 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
袋鼠云数栈UED团队3 小时前
一套 Spec-First 的 AI 编程工作流
前端·人工智能
袋鼠云数栈前端3 小时前
一套 Spec-First 的 AI 编程工作流
前端·ai+
angerdream3 小时前
Android手把手编写儿童手机远程监控App之vue3 路由守卫
前端
不服老的小黑哥3 小时前
AI规范驱动编程-harness工程项目实战
前端
vivo互联网技术3 小时前
从 Web 到桌面:基于 Tauri 2.0 + Vue 3 打造 vivo 线下门店「大头贴」拍照体验系统
前端·rust
光影少年3 小时前
React 合成事件机制、和原生事件区别、事件冒泡阻止
前端·react.js·掘金·金石计划