simple-react-draggable 简单可拖拽的react组件

simple-react-draggable

简单的可拖拽的react组件, 不依赖任何第三方的库 项目地址css

example(demo)

结合antd Select折中的支持排序方案(理想的状况重构Select中Tag组件)

解决什么问题?
  • 来源于运营同窗的需求;
  • 在用antd的select组件时, mode:multiple时, 选中的tag不能排序; 有时须要对选中数据执行调整顺序时, 须要删掉数据,而后在添加数据;

Usage

npm/cnpm i simple-react-draggablereact

  • Props
Attribute Description Type Accepted Values Default
list data Array [{id,name}] []
option config {}
className container style css string any
itemClassName each item style css string any
onMove listener function callback
  • example
import Draggable from 'simple-react-draggable';
  const list = [{
    id: 1,
    name: '测试1'
  },{
    id: 2,
    name: '测试2'
  },{
    id: 3,
    name: '测试3'
  },{
    id: 4,
    name: '测试4'
  },{
    id: 5,
    name: '测试6'
  },{
    id: 7,
    name: '测试7'
  }];

  <Draggable list={list}/> 复制代码
参数说明

配置:git

  • option: (可选)github

    • offsetX: item的左右间隔距离
    • offsetY: item的上下间隔距离
    • width: item的宽度
    • height: item的高度
    • defaultOption:
    const defaultOption = {
            width: 50, 
            height: 20,
            offsetX: 3,
            offsetY: 3
        };
    复制代码
  • itemClassName: (可选)npm

    • 移动项的className
    • 移动项默认的样式
    .draggable-item {
          background: #f3f3f3;
          border-radius: 4px;
          overflow: hidden;
          cursor: default;
          font-size: 12px;
          color: rgba(0,0,0,0.65);
          text-align: center;
        }
    复制代码
  • className: (可选)antd

    • 容器的样式
    • 容器的样式
    .wrapper {
      background: #fff;
      border-radius: 4px;
      border: 1px solid #d9d9d9;
      box-sizing: border-box;
      padding: 0;
      margin: 0;
    }
    复制代码
  • list: 须要排序的数据app

    • type: [{id,name}]
  • onMove: 把移动后的数据返回测试

    • type: callbackui

    • return: [{id, name}]this

实现的思路
  • 总的思路: 找到要移动的数据的index和目标位置的元素的index, but how?

    1. 每个item元素bind onDragStart监听事件; 并记录每一个元素的index(代码为position); 这一步获取移动数据的index
    list.forEach((item,index) => {
       items.push(
         <div key={item.id} onDragStart = {(event) => this.onDragStart(event, index)} draggable style={{marginTop: offsetY, marginLeft: offsetX, width, height, lineHeight: height+'px'}} className={itemCls} > {item.name} </div>
       );
     })
    
     // onDragStart 记录index
      event.dataTransfer.setData("position", position);
    复制代码
    1. 获取目标位置元素的index: 容器中监听onDrop事件, 使用onDragOver防止事件冒泡; onDrap事件实现:
    onDrop = (event) => {
         let {list} = this.state,
             position = event.dataTransfer.getData("position"),
             targetIndex = this.calculateTargetIndex(event.target),
             removeItem;
         let {onMove} = this.props;
         if(targetIndex !== -1){
             removeItem = list.splice(position, 1)[0];
             list.splice(targetIndex, 0, removeItem);
             this.setState({list});
             onMove && onMove(list)
         }
     }
    复制代码
    1. 在React计算元素的物理位置

    1).计算拖拽容器的x,y,以及每一行能显示多少移动元素

    calculateDragContainer(){
         let { option: {offsetX, width} } = this.state;
         let c = ReactDOM.findDOMNode(this.refs.draggableContainer).getBoundingClientRect(),
             col = parseInt((c.width - offsetX) / (width + offsetX));
         return {
         x: c.x,
         y: c.y,
         col
         }
     }
    复制代码

    2). 计算item元素的x,y

    calculateDragItem(target){
            var t = ReactDOM.findDOMNode(target).getBoundingClientRect();
            return {x: t.x, y: t.y}
        }
    复制代码

    3). 计算每个list中的i数据的行列值

    calculateIndexRowCol(i, maxCol) { // index row col
        let row, col;
        row = Math.ceil(i / maxCol);
        col = i % maxCol;
        if(col === 0) {
        col = maxCol;
        }
        return {row, col};
    }
    复制代码

    4). 计算目标位置的list元素的index

    calculateTargetIndex(target){// 获取目标元素的index
        let { list, option: {width, height, offsetX, offsetY} } = this.state,
            c = this.calculateDragContainer(),
            t = this.calculateDragItem(target),
            targetIndex = -1,targetX,targetY, i,
            len = list.length;
        if(t.x !== c.x && list.length > 0){ // 鼠标落在目标item上
        for(i = 1; i <= len; ++i){
            let {row, col} = this.calculateIndexRowCol(i, c.col);
                targetX = ((col - 1) * width + col * offsetX) + c.x;
                targetY = ((row - 1) * height + row * offsetY) + c.y;
            if(targetX === t.x && targetY === t.y){
            targetIndex = i - 1;
            break; 
            }
        }
        }
        return targetIndex;
    }
    复制代码
相关文章
相关标签/搜索