React 实现列表拖动效果
当我们想在 React 中实现一个列表拖动的效果的时候,有很多的第三方库(React dnd)可以借鉴,但是学习第三方库也是一个成本,或者拖动本身并不复杂,只需要第三方库的某一个 api 。这样情况下,我们可以自己实现一个。
 
 组件源码
 完成项目 欢迎 star
 效果预览用户名:admin;密码:admin;
1. 使用 React 的鼠标事件
在这里我们只会用到:onMouseDown、onMouseMove、onMouseUp 这三个鼠标事件
定义组件的 state
 state = {
    list: data, // 列表的数据
    dragging: false, // 是否开始拖`动`
    draggingIndex: -1, // 拖动元素的下标
    startPageY: 0, // 开始拖动的 Y 轴坐标
    offsetPageY: 0 // 拖动元素的位移
  }
2. 判断拖放的开始和结束
onMouseDown开始、onMouseUp结束
- 给列表的每一项添加一个onMouseDown事件监听
<List.Item 
    onMouseDown={(e) => this.dragging(e, index)}
    >
  {item}
</List.Item>
- 当鼠标按下的时候我们初始化组件的状态。
  // 点击的时候记录 Y 轴的位置 
dragging = (e, index) => {
  this.setState({
    dragging: true, 
    draggingIndex: index, 
    currentPageY: e.pageY, // 只需要纵向移动 
    startPageY: e.pageY,
  })
}
- 给点击的元素添加样式,让我们知道要拖动谁。
<List.Item 
    onMouseDown={(e) => this.dragging(e, index)}
   	style={this.getDraggingStyle(index)}
    >
  {item}
</List.Item>
// 移动动画
getDraggingStyle = (index) => {
  if (index !== this.state.draggingIndex) return
  return {
    backgorundColor: '#eee',
    transform: `translate(10px,${this.state.offsetPageY}px)`, // 下面会介绍
    opacity: 0.5
  }
}
3. 实现拖放元素的位移效果
效果:拖放的元素视觉效果上要脱离列表本身,在列表上下进行移动。
如何实现:
-  需要对 onMouseMove进行监听。-  在哪里进行监听 ?列表本身? 首先不能在列表本身进行监听,也不能在父容器监听,因为鼠标移动的范围是屏幕的大小(暂时这么设定),那么只有在 document上进行监听吗 ?其实我们可以设置一个遮罩层,在它上面进行 onMouseMove、onMouseUp的事件监听。<List dataSource={this.state.list} renderItem={(item, index) => ( <List.Item onMouseDown={(e) => this.dragging(e, index)} key={item} style={this.getDraggingStyle(index)} > {item} </List.Item> )} /> {/* 用一个遮罩监听事件,也可以监听整个 document */} { this.state.dragging && ( <div style={maskStyle} onMouseUp={e => this.onMouseUp(e)} onMouseMove={e => this.onMouseMove(e)} > </div> ) }const maskStyle = { position: 'fixed', left: 0, right: 0, top: 0, bottom: 0, backgorund: 'rgba(0,0,0,0.5)' }
 
-  
-  需要记录 onMouseMove移动的轨迹(offset),列表跟随鼠标进行移动。- 需要记录从起点移动到终点的距离(offset),记录移动元素的下标。
- 根据移动距离是否大于行高判断是向上移动,还是向下移动。
- 移动的过程更新 offsetPageY 然后借助 CSS3动画进行移动。
 
// 移动的轨迹
onMouseMove = (e) => {
  let offset = e.pageY - this.state.startPageY
  const draggingIndex = this.state.draggingIndex
  if (offset > lineHeight && draggingIndex < this.state.list.length) {
    //  向下移动
    offset -= lineHeight
  } else if (offset < -lineHeight && draggingIndex > 0) {
    // 向上移动
    offset += lineHeight
  }
  // item 移动的距离
  this.setState({ offsetPageY: offset })
}
4. 更新拖放后的数据
移动的过程中更新列表的数据
// 重新计算数组,插入一个,删除一个。不断地插入删除(每隔一行)。
const move = (arr = [], startIndex, toIndex) => {
  arr = arr.slice()
  arr.splice(toIndex, 0, arr.splice(startIndex, 1))
  return arr;
}
更新数据
onMouseMove = (e) => {
  let offset = e.pageY - this.state.startPageY
  const draggingIndex = this.state.draggingIndex
  if (offset > lineHeight && draggingIndex < this.state.list.length) {
    //  向下移动
    offset -= lineHeight
     // 按照移动的方向进行数据交换
    this.setState({
      list: move(this.state.list, draggingIndex, draggingIndex + 1),
      draggingIndex: draggingIndex + 1,
      startPageY: this.state.startPageY + lineHeight
    })
  } else if (offset < -lineHeight && draggingIndex > 0) {
    // 向上移动
    offset += lineHeight
    this.setState({
      list: move(this.state.list, draggingIndex, draggingIndex - 1),
      draggingIndex: draggingIndex - 1,
      startPageY: this.state.startPageY - lineHeight
    })
  }
向上移动 draggingIndex-1 ,向下移动 draggingIndex+1 。
每移动一行,数据更新一次,0 移动到 2 的位置,需要先移动到 1 ,然后从 1 的位置开始重新计算位移,然后由 1 的位置移动到 2 。以此类推。类似冒泡排序。
5. 结束拖放
// 松开鼠标的时候,重新初始化 startPageY、draggingIndex,
onMouseUp = (e) => {
  this.setState({
    dragging: false,// 移除遮罩
    startPageY: 0, 
    draggingIndex: -1
  })
}
这只是一个很简单场景实现(列表的简单排序),如需功能强大的拖动还是需要第三方库来实现。
 
                   
                   
                   
                   
                             本文详细介绍如何在React中不依赖第三方库实现列表拖动效果,包括使用鼠标事件、实现元素位移及更新数据等关键步骤。
本文详细介绍如何在React中不依赖第三方库实现列表拖动效果,包括使用鼠标事件、实现元素位移及更新数据等关键步骤。
           
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   1205
					1205
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            