美文网首页
使用 react-beautiful-dnd 快速实现可拖拽看板

使用 react-beautiful-dnd 快速实现可拖拽看板

作者: VioletJack | 来源:发表于2020-07-11 16:01 被阅读0次

当我还在使用 react-dnd 设计拖拽逻辑和交互、当我还在为计算拖拽元素和 hover 元素的位置坐标而烦躁不已,当我还在为即将到来的 deadline 发愁之际,我发现了 react-beautiful-dnd。写了个 demo 试了下,真香!

先上代码

话不多说,先上代码。(我等伸手党福音~)

如果懒得看代码,这里是 >>Github 地址<<

安装所需库:

$ yarn add react-beautiful-dnd
$ yarn add @types/react-beautiful-dnd

下面是代码:

import React, { useState } from 'react'
import { DragDropContext, Droppable, Draggable, DropResult, DraggableProvided, DraggableStateSnapshot } from 'react-beautiful-dnd'
import update from 'immutability-helper'
import styles from './index.less'

interface initialDataInferface {
  id: number;
  name: string;
  issues: {
    id: number;
    name: string
  }[]
}

interface ColumnProps {
  columnIndex: number
  column: initialDataInferface
}

interface IssueProps {
  id: number
  issueIndex: number,
  name: string
}

const InitialData: initialDataInferface[] = [
  {
    id: 100,
    name: 'todo',
    issues: [{ id: 1, name: '吃饭' }, { id: 2, name: '睡觉' }, { id: 3, name: '打豆豆' }],
  },
  {
    id: 200,
    name: 'doing',
    issues: [{ id: 4, name: '删库' }, { id: 5, name: '跑路' }]
  },
  {
    id: 300,
    name: 'done',
    issues: []
  }
]

const Issue = (props: IssueProps) => {
  const { id, issueIndex, name } = props

  return (
    <Draggable draggableId={`${id}`} index={issueIndex}>
      {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => (
        <div
          ref={provided.innerRef}
          className={snapshot.isDragging ? styles.issueDragging : styles.issue}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
        >
          {name}
        </div>
      )}
    </Draggable>
  )
}

const Column = (props: ColumnProps) => {
  const { columnIndex, column } = props
  const { issues } = column
  return (
    <div className={styles.column}>
      <div className={styles.columnTitle}>
        {column.name}({column.issues.length})
      </div>
      <Droppable droppableId={`${columnIndex}`}>
        {(provided, snapshot) => (
          <div
            ref={provided.innerRef}
            className={snapshot.isDraggingOver ? styles.columnContentActive : styles.columnContent}
            {...provided.droppableProps}
          >
            {issues.map((issue, index) => (
              <Issue key={issue.id} issueIndex={index} id={issue.id} name={issue.name} />
            ))}
            {provided.placeholder}
          </div>
        )}
      </Droppable>
    </div>
  )
}

export default () => {
  const [data, setData] = useState(InitialData)

  const onDragEnd = (result: DropResult) => {
    const { destination, source } = result
    if (!destination) {
      return
    }

    const fromColumnIndex = Number(source.droppableId)
    const fromIssueIndex = source.index
    const toColumnIndex = Number(destination.droppableId)
    const toIssueIndex = destination.index

    const TempIssue = data[fromColumnIndex].issues[fromIssueIndex]

    let TempData = update(data, {
      [fromColumnIndex]: {
        issues: issues =>
          update(issues, {
            $splice: [[fromIssueIndex, 1]]
          })
      }
    })

    TempData = update(TempData, {
      [toColumnIndex]: {
        issues: issues =>
          update(issues, {
            $splice: [[toIssueIndex, 0, TempIssue]]
          })
      }
    })

    setData(TempData)
  }

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div className={styles.container}>
        {data.map((column, index) => {
          return <Column columnIndex={index} key={column.id} column={column} />
        })}
      </div>
    </DragDropContext>
  )
}

为了好看加了些样式:

.container {
  display: flex;
  height: 640px;
  background: #f7f7f7;
}

.column {
  display: inline-block;
  width: 292px;
  height: 640px;
}

.columnTitle {
  color: #383838;
  font-size: 14px;
  font-weight: 600;
  line-height: 40px;
  margin: 0 10px;
}

.columnContent {
  height: 600px;
  overflow: auto;
}

.columnContentActive {
  overflow: auto;
  height: 600px;
  background: #ccecff;
  border: 2px solid #1b9aee;
}

.issue {
  position: relative;
  min-height: 20px;
  padding: 14px 44px;
  background: #ffffff;
  margin: 8px 10px;
}

.issueDragging {
  position: relative;
  min-height: 20px;
  padding: 14px 44px;
  background: #ffffff;
  opacity: 0.9;
  margin: 8px 10px;
}

效果是这样子的:

示例

因为我看了网上的拖拽 demo 都不够精美,有些代码也比较老了,所以贴个最新的希望能对大家有所帮助吧。

简单介绍 API

详细 API 就不人肉翻译啦~没啥意义,看 >>这里<<就好。

PS:一开始在首页 README 找 API 居然没有找到,却跑到了一个教学视频网站去了。把我给气的……后来才想起来按道理会有一个叫 doc 的目录 - -

简单说下我用到的 API:

  • <DragDropContext /> 是为了给拖拽提供上下文的,只有在 <DragDropContext /> 中去写拖拽才是有效的。这里注意它必须要一个 onDragEnd 的 props 来操作拖拽结束事件。其他事件有如下,具体说用看一眼就知道。
 onBeforeCapture = () => {
    /*...*/
  };

  onBeforeDragStart = () => {
    /*...*/
  };

  onDragStart = () => {
    /*...*/
  };
  onDragUpdate = () => {
    /*...*/
  };
  onDragEnd = () => {
    // the only one that is required
  };

  render() {
    return (
      <DragDropContext
        onBeforeCapture={this.onBeforeCapture}
        onBeforeDragStart={this.onBeforeDragStart}
        onDragStart={this.onDragStart}
        onDragUpdate={this.onDragUpdate}
        onDragEnd={this.onDragEnd}
      >
        <div>Hello world</div>
      </DragDropContext>
    );
  }
  • 然后是 <Droppable />,它用来定义放置拖拽元素的容器。它有一个必填属性 droppableId,另外它的 children 属性被定义成了一个函数,函数提供的 provided 用来绑定 DOM 节点,提供的 snapshot 可以让我们获取当前放置容器的属性和状态。
import { Droppable } from 'react-beautiful-dnd';

<Droppable droppableId="droppable-1" type="PERSON">
  {(provided, snapshot) => (
    <div
      ref={provided.innerRef}
      style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
      {...provided.droppableProps}
    >
      <h2>I am a droppable!</h2>
      {provided.placeholder}
    </div>
  )}
</Droppable>;
  • <Draggable /> 包含的东西就是那个拖拽元素啦。它必须包含有 draggableIdindexchildren 三个属性。它和 <Droppable /> 类似也是将 children 定义为了函数,函数提供的 provided 用来绑定 DOM 节点,提供的 snapshot 可以让我们获取当前放置容器的属性和状态。
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>;

注意,三者是包含关系,必须逐层实现才能够做到拖拽哦。所以呈现的 DOM 结构样子应该是酱紫的:

<DragDropContext>
  <Droppable>
      <Draggable></Draggable>
      <Draggable></Draggable>
      <Draggable></Draggable>
      ......
  </Droppable>
  <Droppable>
      <Draggable></Draggable>
      <Draggable></Draggable>
      ......
  </Droppable>
  ......
</DragDropContext>

相关文章

  • 使用 react-beautiful-dnd 快速实现可拖拽看板

    当我还在使用 react-dnd 设计拖拽逻辑和交互、当我还在为计算拖拽元素和 hover 元素的位置坐标而烦躁不...

  • react-beautiful-dnd导致页面卡死

    react-beautiful-dnd是一个很强大的拖拽组件。最近项目中使用react-beautiful-dnd...

  • react拖拽功能实现

    因项目中有拖拽功能需求,于是乎在github上找到了react-beautiful-dnd这个react列表拖拽库...

  • HTML5实现拖拽

    实现拖拽效果源元素 - 要拖拽的文件目标元素 - 要拖拽到哪里去 目前实现拖拽效果使用原生DOM就能实现 - 最麻...

  • vdesjs实现原理

    拖拽功能实现 vdesjs的拖拽功能使用到了vuedraggble,vuedragable的实现是基于sortab...

  • POS-2017

    拖拽排序功能 实现方法: 使用jquery的Sortable功能可以实现拖拽功能 index页面 html部分 商...

  • Flutter使用Draggable实现可拖拽GridView

    本例通过继承StatefulWidget,使用Draggable和GridView使GridView的Item实现...

  • UICollectionView 拖拽重排

    iOS9之后,UICollectionView使用API可实现拖拽重排,粘贴的工程中,通过push跳转到"Coll...

  • 实现可拖拽插件

    一、js拖拽插件的原理常见的拖拽操作是什么样的呢?整过过程大概有下面几个步骤: 1、用鼠标点击被拖拽的元素 2、按...

  • PaperLike

    上周有翻译到一篇关于使用Layout实现可拖拽的CollectionView的英文博客,之后就去大概实现了一下Fa...

网友评论

      本文标题:使用 react-beautiful-dnd 快速实现可拖拽看板

      本文链接:https://www.haomeiwen.com/subject/yfzhcktx.html