React implements the list drag effect

When we want to implement a list drag effect in React, there are many third-party libraries to learn from. However, learning from third-party libraries is also a cost, or the drag itself is not complicated, only a third-party library API is required. In that case, we can implement one ourselves.

Component source

Effect preview user name: admin; Password: admin;

1. Use the React mouse event

React Mouse Event

The only mouse events we will use here are onMouseDown, onMouseMove, and onMouseUp

Define the state of the component

 state = {
    list: data, // List data
    dragging: false.// Whether to start dragging
    draggingIndex: - 1.// Drag the subscript of the element
    startPageY: 0.// Start dragging the y-coordinate
    offsetPageY: 0 // Drag the displacement of the element
  }
Copy the code

2. Determine the start and end of drag and drop

OnMouseDown starts and onMouseUp ends

  1. Add one to each item in the listonMouseDownEvent listeners
<List.Item 
    onMouseDown={(e) => this.dragging(e, index)}
    >
  {item}
</List.Item>
Copy the code
  1. We initialize the state of the component when the mouse is pressed.
  // Record the position of the Y axis when clicked
dragging = (e, index) = > {
  this.setState({
    dragging: true.draggingIndex: index, 
    currentPageY: e.pageY, // Just move vertically
    startPageY: e.pageY,
  })
}
Copy the code
  1. Add styles to the clicked elements to let us know who to drag.
<List.Item 
    onMouseDown={(e) => this.dragging(e, index)}
   	style={this.getDraggingStyle(index)}
    >
  {item}
</List.Item>
Copy the code
// Move the animation
getDraggingStyle = (index) = > {
  if(index ! = =this.state.draggingIndex) return
  return {
    backgorundColor: '#eee'.transform: `translate(10px,The ${this.state.offsetPageY}px)`.// This will be explained below
    opacity: 0.5}}Copy the code

3. Achieve the displacement effect of drag and drop elements

Effect: Drag and drop elements visually away from the list itself, moving up and down the list.

How to achieve:

  1. You need to listen on onMouseMove.

    1. Where is the listening? The list itself?

      First of all, you can’t listen on the list itself, or on the parent container, because the mouse movement is the size of the screen (for now), so only on the Document?

      In fact, we can set up a mask layer on which to listen for onMouseMove and onMouseUp events.

      <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>} /> {/* Listen on events with a mask, or listen on the whole document */} {this.state.dragging && (<div
            style={maskStyle}
            onMouseUp={e= > this.onMouseUp(e)}
            onMouseMove={e => this.onMouseMove(e)}
            >
          </div>)}Copy the code
      const maskStyle = {
        position: 'fixed'.left: 0.right: 0.top: 0.bottom: 0.backgorund: 'rgba (0,0,0,0.5)'
      }
      Copy the code
  2. The offset of onMouseMove movement needs to be recorded. The list moves with the mouse.

    1. You need to record the distance moved from the start to the end (offset) and the subscript of the moved element.
    2. Determine whether to move up or down based on whether the distance moved is greater than the row height.
    3. The move process updates offsetPageY then with the aid ofCSS3Animation moves.
// The moving trace
onMouseMove = (e) = > {
  let offset = e.pageY - this.state.startPageY
  const draggingIndex = this.state.draggingIndex
  if (offset > lineHeight && draggingIndex < this.state.list.length) {
    // Move down
    offset -= lineHeight
  } else if (offset < -lineHeight && draggingIndex > 0) {
    // Move up
    offset += lineHeight
  }
  // The distance the item moves
  this.setState({ offsetPageY: offset })
}
Copy the code

4. Update the drag-and-drop data

Update the list of data during movement

// recalculate the array, insert one, delete one. Keep inserting and deleting (every other line).
const move = (arr = [], startIndex, toIndex) = > {
  arr = arr.slice()
  arr.splice(toIndex, 0, arr.splice(startIndex, 1))
  return arr;
}
Copy the code

Update the data

onMouseMove = (e) = > {
  let offset = e.pageY - this.state.startPageY
  const draggingIndex = this.state.draggingIndex
  if (offset > lineHeight && draggingIndex < this.state.list.length) {
    // Move down
    offset -= lineHeight
     // Exchange data in the direction of movement
    this.setState({
      list: move(this.state.list, draggingIndex, draggingIndex + 1),
      draggingIndex: draggingIndex + 1.startPageY: this.state.startPageY + lineHeight
    })
  } else if (offset < -lineHeight && draggingIndex > 0) {
    // Move up
    offset += lineHeight
    this.setState({
      list: move(this.state.list, draggingIndex, draggingIndex - 1),
      draggingIndex: draggingIndex - 1.startPageY: this.state.startPageY - lineHeight
    })
  }
Copy the code

DraggingIndex up -1, draggingIndex down +1.

The data is updated every time a row is moved. 0 is moved to position 2, so it needs to be moved to position 1 first, then recalculated from position 1, and then moved from position 1 to position 2. And so on. Sort of bubble sort.

5. End drag and drop

// While releasing the mouse, reinitialize startPageY, draggingIndex,
onMouseUp = (e) = > {
  this.setState({
    dragging: false.// Remove the mask
    startPageY: 0.draggingIndex: - 1})}Copy the code

This is a very simple scenario implementation (simple sorting of lists) and requires a third party library to implement powerful dragging.