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
- Add one to each item in the list
onMouseDown
Event listeners
<List.Item
onMouseDown={(e) => this.dragging(e, index)}
>
{item}
</List.Item>
Copy the code
- 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
- 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:
-
You need to listen on onMouseMove.
-
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
-
-
The offset of onMouseMove movement needs to be recorded. The list moves with the mouse.
- You need to record the distance moved from the start to the end (offset) and the subscript of the moved element.
- Determine whether to move up or down based on whether the distance moved is greater than the row height.
- The move process updates offsetPageY then with the aid of
CSS3
Animation 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.