React-Beautiful-DND
,一個強大的拖拽包,可以優雅的作出豐富的拖拽頁面應用,適用於列表之間拖拽的場景,支持移動端,且簡單易上手。官方豐富的案例展現,正合心意。css
import { DragDropContext } from 'react-beautiful-dnd';
const App = () => {
const onDragStart = () => {
/*...*/
};
const onDragUpdate = () => {
/*...*/
};
const onDragEnd = () => {
// the only one that is required
};
return (
<DragDropContext onDragStart={onDragStart} onDragUpdate={onDragUpdate} onDragEnd={onDragEnd} > <div>Hello world</div> </DragDropContext>
);
};
複製代碼
構建一個能夠拖拽的範圍,把你想可以拖放的 react 代碼放到 DragDropContext
中。react
支持的事件:git
onDragStart
:拖拽開始回調github
onDragUpdate
:拖拽中的回調npm
onDragEnd
:拖拽結束時的回調api
const onDragEnd = (result) => {
console.log(result);
/* { draggableId: "item-3", // 從這裏 source:{ droppableId: "droppable", index: 2 }, // 移到這裏 destination:{ droppableId: "droppable", index: 1 } } */
};
複製代碼
WARNING:
DragDropContext
不支持嵌套,且必須設置DranDropContext
的onDragEnd
鉤子函數(拖拽後的數組從新排序操做在這裏進行)數組
import { Droppable } from 'react-beautiful-dnd';
<Droppable droppableId="droppable-1"> {(provided, snapshot) => ( <div ref={provided.innerRef} style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }} {...provided.droppableProps} > <h2>I am a droppable!</h2> {provided.placeholder} </div> )} </Droppable>;
複製代碼
構建一個能夠被拖拽放入的區域塊。markdown
參數介紹:app
DroppableId
: 此屬性是必須的,用於惟一標識,不要更改此 ID。direction
:vertical
(垂直拖拽,默認)/ horizontal
(水平拖拽)type
:指定能夠被拖動的元素 classDroppable
的 React 子元素必須是返回 ReactElement
的函數。該函數提供了兩個參數:provided
和 snapshot
:ide
provided.innerRef
: 必須綁定到 ReactElement 中最高的 DOM 節點。
provided.droppableProps
: Object,包含須要應用於 Droppable 元素的屬性,包含一個數據屬性,能夠用它來控制一些不可見的 CSS
console.log(provided.droppableProps);
/* { data-rbd-droppable-context-id: "1", data-rbd-droppable-id: "droppable" } */
複製代碼
provided.placeholder
: 佔位符
snapshot
: 當前拖動狀態,能夠用來在被拖動時改變 Droppable 的外觀。
console.log(snapshot);
/* { draggingFromThisWith: null, draggingOverWith: null, isDraggingOver: false, 拖拽狀態 isUsingPlaceholder: false } */
複製代碼
import { Draggable } from 'react-beautiful-dnd';
<Draggable draggableId="draggable-1" index={0}> {(provided, snapshot) => ( <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} > <h4>My draggable</h4> </div> )} </Draggable>;
複製代碼
可被拖拽的元素,<Draggable />
必須始終包含在 <Droppable />
中,能夠在 <Droppable />
內從新排序 <Draggable />
或移動到另外一個 <Droppable />
。
參數介紹:
draggableId
:string
,必需,做爲惟一標識符。index
:number
,匹配 Droppable
中 Draggable
的順序。是列表中 Draggable
的惟一索引,索引數組必須是連續的,好比 [0,1,2]
。isDragDisabled
: 默認 false
,一個可選標誌,用於控制是否容許 Draggable
拖動。<Droppable />
。效果預覽:
# yarn
yarn add react-beautiful-dnd
# npm
npm install react-beautiful-dnd --save
複製代碼
使用 react-beautiful-dnd
:
import { DragDropContext } from 'react-beautiful-dnd';
複製代碼
└── dndPro
├── component
├── Column.js
├── Item.js
├── ItemList.js
└── Row.js
├── get-initial-data.js // 初始數據
├── style.css // 樣式文件
└── index.js
複製代碼
咱們要實現三種拖拽需求:容器可拖拽、元素可穿梭容器拖拽 和 容器內部元素的垂直拖拽。咱們將經過兩層不通拖拽方向的 <Droppable direction="?" />
和虛擬列表模式實現。
<DragDropContext />
包裹在最外層,構建一個能夠拖拽的範圍;添加第一個 <Droppable />
,一個能夠被拖拽放入的區域塊,並指定拖拽方向爲水平(horizontal
)實現容器見的拖拽,指定拖拽類型爲 column
(只有 className='column'
元素可拖拽)。根據 columnOrder: ['column-0', 'column-1']
渲染兩個 Column
組件。
// index.js
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
<DragDropContext onDragEnd={onDragEnd}> <div className="dnd-pro"> <Droppable droppableId="all-droppables" direction="horizontal" type="column" > {(provided) => ( <div className="columns" {...provided.droppableProps} ref={provided.innerRef} > {state.columnOrder.map((columnId, index) => ( <Column key={columnId} column={state.columns[columnId]} index={index} /> ))} {provided.placeholder} </div> )} </Droppable> </div> </DragDropContext>;
複製代碼
Column
組件就是一個 <Draggable />
元素,到這裏就實現了容器可拖拽,是否是很 easy ~
// Column.js
import { Draggable } from 'react-beautiful-dnd';
<Draggable draggableId={column.id} index={index}> {(provided) => ( <div className="column" {...provided.draggableProps} ref={provided.innerRef} > <h3 className="column-title" {...provided.dragHandleProps}> {column.title} </h3> <ItemList column={column} index={index} /> </div> )} </Draggable>;
複製代碼
react-beautiful-dnd
支持在虛擬列表內和虛擬列表之間拖放。通常狀況,建議列表的規格不超過 500 個元素。虛擬列表,是對窗口性能的一種優化,詳細介紹能夠看這邊,咱們直接來看看用法吧:
虛擬列表的行爲與常規列表不一樣。咱們須要告訴 rbd
咱們的列表是虛擬列表。
<Droppable droppableId="droppable" mode="virtual">
{/*...*/}
</Droppable>
複製代碼
拖動項目的副本
<Droppable
droppableId="droppable"
mode="virtual"
renderClone={(provided, snapshot, rubric) => (
<div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} > Item id: {items[rubric.source.index].id} </div>
)}
>
{/*...*/}
</Droppable>
複製代碼
須要注意的是,placeholder
在虛擬列表中會出現問題。由於虛擬列表的長度再也不取決於元素的集體長度,而是計算視覺長度,因此咱們須要作一些處理:
const itemCount = snapshot.isUsingPlaceholder
? column.items.length + 1
: column.items.length;
複製代碼
對虛擬列表有初步瞭解後,咱們再回到 <ItemList />
來。
新開闢一個能夠被拖拽放入的區域塊 <Droppable />
,拖拽方向取垂直(默認),指定虛擬列表模式(mode="virtual"
),發生拖拽時,使用元素副本(renderClone
)。
// ItemList.js
import { FixedSizeList } from 'react-window';
import { Droppable } from 'react-beautiful-dnd';
<Droppable droppableId={column.id} mode="virtual" renderClone={(provided, snapshot, rubric) => ( <Item provided={provided} isDragging={snapshot.isDragging} item={column.items[rubric.source.index]} /> )} > {(provided, snapshot) => { const itemCount = snapshot.isUsingPlaceholder ? column.items.length + 1 : column.items.length; return ( <FixedSizeList height={500} itemCount={itemCount} itemSize={80} width={300} outerRef={provided.innerRef} itemData={column.items} className="task-list" ref={listRef} > {Row} </FixedSizeList> ); }} </Droppable>;
複製代碼
<Item />
組件和 <Row />
組件都是一個 Draggable 元素。
容器級別的拖拽和同容器內的元素拖拽,簡單的交換元素便可:
// reordering list
if (result.type === 'column') {
const columnOrder = reorderList(
state.columnOrder,
result.source.index,
result.destination.index,
);
setState({
...state,
columnOrder,
});
return;
}
// reordering in same list
if (result.source.droppableId === result.destination.droppableId) {
const column = state.columns[result.source.droppableId];
const items = reorderList(
column.items,
result.source.index,
result.destination.index,
);
const newState = {
...state,
columns: {
...state.columns,
[column.id]: {
...column,
items,
},
},
};
setState(newState);
return;
}
複製代碼
元素跨容器拖拽,分爲兩步:
// moving between lists
const sourceColumn = state.columns[result.source.droppableId];
const destinationColumn = state.columns[result.destination.droppableId];
const item = sourceColumn.items[result.source.index];
// 1. remove item from source column
const newSourceColumn = {
...sourceColumn,
items: [...sourceColumn.items],
};
newSourceColumn.items.splice(result.source.index, 1);
// 2. insert into destination column
const newDestinationColumn = {
...destinationColumn,
items: [...destinationColumn.items],
};
newDestinationColumn.items.splice(result.destination.index, 0, item);
const newState = {
...state,
columns: {
...state.columns,
[newSourceColumn.id]: newSourceColumn,
[newDestinationColumn.id]: newDestinationColumn,
},
};
setState(newState);
複製代碼