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;
    }
相关推荐
前端世界3 小时前
前端路由切换不再白屏:React/Vue 实战优化全攻略(含可运行 Demo)
前端·vue.js·react.js
Prosper Lee3 小时前
前端基础(四十三):文本数据解析为键值对
开发语言·前端·javascript
我的收藏手册3 小时前
Web与Nginx网站服务
运维·前端·nginx
JarvanMo3 小时前
Flutter 应用程序中的无声杀手:为什么每个开发者都害怕这个文件
前端
小桥风满袖3 小时前
极简三分钟ES6 - 数组遍历
前端·javascript
艾小码3 小时前
彻底搞懂 Vue 生命周期:从 created 到 unmounted 的完整指南
前端·javascript·vue.js
GHOME3 小时前
复习-网络协议
前端·网络协议·面试
秦清3 小时前
组态可视化软件【导入属性】
前端·javascript·后端
小桥风满袖3 小时前
极简三分钟ES6 - 函数的参数
前端·javascript