本文主要介紹一下React實現列表拖拽排序方法,具體樣式如下圖
首先,簡單展示一下組件的數據結構
const CodeSetting = props => {const {$t, // 國際化翻譯函數vm, // 視圖模型數據vm: {CodeSet: { Enable = [], // 啟用的編碼列表Disable = [] // 停用的編碼列表}},getConfig, // 獲取配置的函數save, // 保存配置的函數vmChange // 更新視圖模型的函數} = props;
};
完整的數據結構示例
const vm = {CodeSet: {Enable: [{ Compression: "H.264" },{ Compression: "H.265" },{ Compression: "MPEG-4" }],Disable: [{ Compression: "AVC" },{ Compression: "HEVC" }]}
};
主要用到的代碼如下,簡單看后我將介紹拖拽方法
{Enable.length ? (<Cardtitle={`${$t('com.EnableCode')} (${Enable.length})`}extra={<Buttonsize='small'className='clear-all-btn'type='link'onClick={clearAllEnabled}>{$t('com.ClearAll')}</Button>}>{Enable.map((item, index) => (<divkey={index}className='drag-item'draggableonDragStart={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'><Iconcomponent={remove}onClick={() => codeSetChange('remove', index)}style={{fontSize: '20px'}}/></div></div>))}</Card>
首先用到的組件是Card組件,title是card標題,extra是card后綴
之后遍歷Enable數組,將拿到的每一個值渲染到card中
這個組件實現了 HTML5 原生拖拽 API 來實現編碼列表的拖拽排序功能。主要使用了以下拖拽事件:
onDragStart - 拖拽開始
onDragOver - 拖拽懸停
onDrop - 拖拽放置
onDragEnd - 拖拽結束
狀態管理
const [draggedIndex, setDraggedIndex] = useState(null); // 記錄當前拖拽項的索引
拖拽事件處理函數
1 拖拽開始 (handleDragStart)
const handleDragStart = (e, index) => {setDraggedIndex(index); // 記錄拖拽項的索引e.dataTransfer.effectAllowed = 'move'; // 設置拖拽效果為移動e.currentTarget.classList.add('dragging'); // 添加拖拽樣式
};
2 拖拽懸停 (handleDragOver)
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)
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)
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 結構
<divkey={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. 核心算法
- 拖拽排序的核心算法是數組重排序:
- 獲取拖拽項:從原位置取出拖拽的元素
- 移除拖拽項:在原位置刪除該元素
- 插入新位置:在目標位置插入該元素
- 更新狀態:將新的數組順序更新到組件狀態
7. 樣式處理
組件通過 CSS 類名來管理拖拽狀態:
.dragging - 拖拽中的樣式
.drag-over - 拖拽懸停的樣式
.drag-item - 可拖拽項的基礎樣式
8.樣式代碼
// 拖拽項容器.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;}