React簡單的可拖拽組件

簡單的可拖拽的react組件, 不依賴任何第三方的庫

解決什麼問題?react

需求來源: [項目地址](https://github.com/marspal/simple-react-draggable)git

來源於運營同窗的需求, 在用[antd](https://github.com/ant-design/ant-design)的select組件時, mode:multiple時, 選中的tag不能排序; 有時須要對選中的數據執行調整順序時, 須要刪掉數據,而後在添加數據; 因爲有時候tag太多, 使得對這些tag排序成爲提升工做效率的迫切要求; 因爲Select組件不支持排序、折中的方案是設計一個組件支持排序,而後把數據同步到Select組件;github

option: (可選)bash

- offsetX: 每個item的左右間隔距離antd

- offsetY: 每個item的上下間隔距離app

- width: 每個item的寬度ui

- height: 每個item的高度this

- defaultOption:spa

const defaultOption = {  width: 50,   height: 20,  offsetX: 3,  offsetY: 3};複製代碼

itemClassName: 每個item的樣式設計

.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: container元素的樣式

.wrapper {    
    background: #fff;
    border-radius: 4px;
    border: 1px solid #d9d9d9; 
    box-sizing: border-box;
    padding: 0;    
    margin: 0;
}複製代碼

list: [{id,name}]

onMove: 提升監聽的事件, 返回拖拽後的數據


實現思路

找到要移動的數據的index和目標位置的元素的index, but how?

每個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>      );    })複製代碼

獲取目標位置元素的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)    }  }複製代碼

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