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;
    }
    複製代碼
相關文章
相關標籤/搜索