使用react-beautiful-dnd实现列表间拖拽踩坑

2022-04-15 0 394
目录
  • 为什么选用react-beautiful-dnd
  • 基本使用方法
    • 基本概念
    • 使用方法
  • 使用过程中遇到的问题
    • 总结
      • 参考资料

        为什么选用react-beautiful-dnd

        相比于react-dnd,react-beautiful-dnd更适用于列表之间拖拽的场景,支持移动端,且较为容易上手。

        基本使用方法

        基本概念

        • DragDropContext:构建一个可以拖拽的范围
        • onDragStart:拖拽开始回调
        • onDragUpdate:拖拽中的回调
        • onDragEnd:拖拽结束时的回调
        • Droppable – 可以放置拖拽块的区域
        • 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>
            );
          }
        }

        确定可放置区域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(字符串),用于唯一标识应用程序的droppable。不要更改此ID特别是在拖动时
        • provided.placeholder: 占位符(这个占位符是默认的,一般不咋符合需求)
        • snapshot: 当前拖动状态,可以用来在被拖动时改变Droppable的外观

        在Dropppable区域使用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,特别是在拖动时

        拖拽结束时,改变源数据

        onDragEnd = result => {
          const { source, destination, draggableId } = result;
          if (!destination) {
            return;
          }
        
          // 修改源和目标数组,将拖拽元素从源数组中删除,再插入到目标数组中
          this.setState({
            xxx: xxx,
          });
        }
        
        

        使用过程中遇到的问题

        向拖拽的目标区域增加自定义占位符(custom placeholder)

        react-beautiful-dnd在拖拽到目标区域时,目标区域的元素之间会给当前拖拽元会自动空出一段space,这段space的距离是目标区域Draggable元素的大小(但不包括元素的margin边距,这也是一个坑,下文会说到解决方法)。

        因此可以在这段距离中采用绝对定位,增加自定义占位符。具体做法:计算出当前自定义占位符元素的left & top距离,在dragUpdate事件中更新这两个距离,可参考beatiful-dnd-custom-placeholder-demo

        拖拽时,修改拖拽元素的transform属性,导致拖拽会卡死在某处,拖拽元素放置位置错误

        在官方文档中,有这样一段说明, 大概是说draggable元素采用了position: fixed定位,但会受到transform会影响。

        #### 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给拖动元素挂在空的父元素上,可参考issue: transform on parent messes up dragging positioning

        但是这个方法并不能解决我的问题,因为还有自定义placeholder的需求。在拖拽时还需要计算placeholder的left的距离,也就需要获取当前拖拽元素的parentNode下的子元素,使用createPortal则获取不到拖拽元素的原parentNode,因此放弃createPortal的方案。采用改变width和height达到transform:scale的效果。

        移动端拖拽元素需要长按该元素(long-press)

        官方文档中给出的说明是,在移动端场景下,在draggable元素上的手指操作,无法确定是tap,force press,或者scroll,所以需要长按该元素才能确定是拖拽。

        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)

        拖拽某个元素悬停在目标位置时,空出的插入space距离不准确的问题
        这个就是上文中提到的,Draggable之间留的placeholder的空余距离是一个Draggable的距离,但不包括Dragglable的margin边距,可参考这个issue。

        最后采用padding来控制Draggable之间的距离,这样在拖拽时空出的space就包括了padding。

        总结

        react-beautiful-dnd比较容易上手, 到2021年3月发布了v13.1.0较为活跃, 以上踩过的坑,希望对大家有所帮助。

        参考资料

        官网beautiful-dnd
        react-beautiful-dnd入门教程

        到此这篇关于使用react-beautiful-dnd实现列表间拖拽踩坑 的文章就介绍到这了,更多相关react 列表拖拽内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!

        免责声明:
        1、本网站所有发布的源码、软件和资料均为收集各大资源网站整理而来;仅限用于学习和研究目的,您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 不得使用于非法商业用途,不得违反国家法律。否则后果自负!

        2、本站信息来自网络,版权争议与本站无关。一切关于该资源商业行为与www.niceym.com无关。
        如果您喜欢该程序,请支持正版源码、软件,购买注册,得到更好的正版服务。
        如有侵犯你版权的,请邮件与我们联系处理(邮箱:skknet@qq.com),本站将立即改正。

        NICE源码网 JavaScript 使用react-beautiful-dnd实现列表间拖拽踩坑 https://www.niceym.com/27825.html