使用react-beautiful-dnd實(shí)現(xiàn)列表間拖拽踩坑
為什么選用react-beautiful-dnd
相比于react-dnd,react-beautiful-dnd更適用于列表之間拖拽的場(chǎng)景,支持移動(dòng)端,且較為容易上手。
基本使用方法
基本概念
- DragDropContext:構(gòu)建一個(gè)可以拖拽的范圍
- onDragStart:拖拽開(kāi)始回調(diào)
- onDragUpdate:拖拽中的回調(diào)
- onDragEnd:拖拽結(jié)束時(shí)的回調(diào)
- Droppable - 可以放置拖拽塊的區(qū)域
- Draggalbe - 可被拖拽的元素
使用方法
把你想能夠拖放的代碼放到DragDropContext中
import { DragDropContext } from 'react-beautiful-dnd'; class App extends React.Component { onDragStart = () => { /*...*/ }; onDragUpdate = () => { /*...*/ } onDragEnd = () => { // the only one that is required }; render() { return ( <DragDropContext onDragStart={this.onDragStart} onDragUpdate={this.onDragUpdate} onDragEnd={this.onDragEnd} > <div>Hello world</div> </DragDropContext> ); } }
確定可放置區(qū)域Dropppable
import { DragDropContext, Droppable } from 'react-beautiful-dnd'; class App extends React.Component { // ... render() { return ( <DragDropContext onDragStart={this.onDragStart} onDragUpdate={this.onDragUpdate} onDragEnd={this.onDragEnd} > <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> </DragDropContext> ); } }
- 必需的DroppableId(字符串),用于唯一標(biāo)識(shí)應(yīng)用程序的droppable。不要更改此ID特別是在拖動(dòng)時(shí)
- provided.placeholder: 占位符(這個(gè)占位符是默認(rèn)的,一般不咋符合需求)
- snapshot: 當(dāng)前拖動(dòng)狀態(tài),可以用來(lái)在被拖動(dòng)時(shí)改變Droppable的外觀
在Dropppable區(qū)域使用Draggable包裹拖拽元素
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; class App extends React.Component { // ... render() { return ( <DragDropContext onDragStart={this.onDragStart} onDragUpdate={this.onDragUpdate} onDragEnd={this.onDragEnd} > <Droppable droppableId="droppable-1"> {(provided, snapshot) => ( <div ref={provided.innerRef} style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }} {...provided.droppableProps} > <Draggable draggableId="draggable-1" index={0}> {(provided, snapshot) => ( <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} > <h4>My draggable</h4> </div> )} </Draggable> {provided.placeholder} </div> )} </Droppable> </DragDropContext> ); } }
- Draggable必須始終包含在Droppable中
- DraggablebId(字符串):必須存在唯一ID,和index(如果為遍歷 key也需要)不要更改此ID,特別是在拖動(dòng)時(shí)
拖拽結(jié)束時(shí),改變?cè)磾?shù)據(jù)
onDragEnd = result => { const { source, destination, draggableId } = result; if (!destination) { return; } // 修改源和目標(biāo)數(shù)組,將拖拽元素從源數(shù)組中刪除,再插入到目標(biāo)數(shù)組中 this.setState({ xxx: xxx, }); }
使用過(guò)程中遇到的問(wèn)題
向拖拽的目標(biāo)區(qū)域增加自定義占位符(custom placeholder)
react-beautiful-dnd在拖拽到目標(biāo)區(qū)域時(shí),目標(biāo)區(qū)域的元素之間會(huì)給當(dāng)前拖拽元會(huì)自動(dòng)空出一段space,這段space的距離是目標(biāo)區(qū)域Draggable元素的大?。ǖ话ㄔ氐膍argin邊距,這也是一個(gè)坑,下文會(huì)說(shuō)到解決方法)。
因此可以在這段距離中采用絕對(duì)定位,增加自定義占位符。具體做法:計(jì)算出當(dāng)前自定義占位符元素的left & top距離,在dragUpdate事件中更新這兩個(gè)距離,可參考beatiful-dnd-custom-placeholder-demo
拖拽時(shí),修改拖拽元素的transform屬性,導(dǎo)致拖拽會(huì)卡死在某處,拖拽元素放置位置錯(cuò)誤
在官方文檔中,有這樣一段說(shuō)明, 大概是說(shuō)draggable元素采用了position: fixed定位,但會(huì)受到transform會(huì)影響。
#### Warning: `position: fixed`
`react-beautiful-dnd` uses `position: fixed` to position the dragging element. This is quite robust and allows for you to have `position: relative | absolute | fixed` parents. However, unfortunately `position:fixed` is [impacted by `transform`](http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/) (such as `transform: rotate(10deg);`). This means that if you have a `transform: *` on one of the parents of a `<Draggable />` then the positioning logic will be incorrect while dragging. Lame! For most consumers this will not be an issue.
To get around this you can [reparent your <Draggable />](/docs/guides/reparenting.md). We do not enable this functionality by default as it has performance problems.
提供了如下解決方法:使用createPortal給拖動(dòng)元素掛在空的父元素上,可參考issue: transform on parent messes up dragging positioning
但是這個(gè)方法并不能解決我的問(wèn)題,因?yàn)檫€有自定義placeholder的需求。在拖拽時(shí)還需要計(jì)算placeholder的left的距離,也就需要獲取當(dāng)前拖拽元素的parentNode下的子元素,使用createPortal則獲取不到拖拽元素的原parentNode,因此放棄createPortal的方案。采用改變width和height達(dá)到transform:scale的效果。
移動(dòng)端拖拽元素需要長(zhǎng)按該元素(long-press)
官方文檔中給出的說(shuō)明是,在移動(dòng)端場(chǎng)景下,在draggable元素上的手指操作,無(wú)法確定是tap,force press,或者scroll,所以需要長(zhǎng)按該元素才能確定是拖拽。
Starting a drag: long press
A user can start a drag by holding their finger 👇 on an element for a small period of time 🕑 (long press)
拖拽某個(gè)元素懸停在目標(biāo)位置時(shí),空出的插入space距離不準(zhǔn)確的問(wèn)題
這個(gè)就是上文中提到的,Draggable之間留的placeholder的空余距離是一個(gè)Draggable的距離,但不包括Dragglable的margin邊距,可參考這個(gè)issue。
最后采用padding來(lái)控制Draggable之間的距離,這樣在拖拽時(shí)空出的space就包括了padding。
總結(jié)
react-beautiful-dnd比較容易上手, 到2021年3月發(fā)布了v13.1.0較為活躍, 以上踩過(guò)的坑,希望對(duì)大家有所幫助。
參考資料
官網(wǎng)beautiful-dnd
react-beautiful-dnd入門教程
到此這篇關(guān)于使用react-beautiful-dnd實(shí)現(xiàn)列表間拖拽踩坑 的文章就介紹到這了,更多相關(guān)react 列表拖拽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-native android狀態(tài)欄的實(shí)現(xiàn)
這篇文章主要介紹了react-native android狀態(tài)欄的實(shí)現(xiàn),使?fàn)顟B(tài)欄顏色與App顏色一致,使用戶界面更加整體。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06React星星評(píng)分組件的實(shí)現(xiàn)
評(píng)分插件在購(gòu)物的應(yīng)用中經(jīng)常可以看得到,但是用著別人的總是沒(méi)有自己寫的順手,本文就使用React實(shí)現(xiàn)星星評(píng)分組件,感興趣的可以了解一下2021-06-06從零開(kāi)始最小實(shí)現(xiàn)react服務(wù)器渲染詳解
這篇文章主要介紹了從零開(kāi)始最小實(shí)現(xiàn)react服務(wù)器渲染詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01React18系列commit從0實(shí)現(xiàn)源碼解析
這篇文章主要為大家介紹了React18系列commit從0實(shí)現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01使用React hook實(shí)現(xiàn)remember me功能
相信大家在使用 React 寫頁(yè)面的時(shí)候都遇到過(guò)完成 Remember me 的需求吧!本文就將這個(gè)需求封裝在一個(gè) React hook 中以供后續(xù)的使用,覺(jué)得有用的同學(xué)可以收藏起來(lái)以備不時(shí)之需,感興趣的小伙伴跟著小編一起來(lái)看看吧2024-04-04React組件中使用JSON數(shù)據(jù)文件的方法詳解
要在 React 組件中使用 JSON 數(shù)據(jù),有多種方法,這篇文章主要為大家詳細(xì)介紹了五個(gè)常見(jiàn)的方法,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下2024-01-01react-native中ListView組件點(diǎn)擊跳轉(zhuǎn)的方法示例
ListView作為React Native的核心組件,用于高效地顯示一個(gè)可以垂直滾動(dòng)的變化的數(shù)據(jù)列表。下面這篇文章主要給大家介紹了關(guān)于react-native中ListView組件點(diǎn)擊跳轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-09-09