Draggable Note Cards
Now that our note cards are properly styled and placed in their rightful positions on screen, it's time to make them draggable.
Dynamic Card Position
To update a note position on the screen, we will need to change the position
attribute from a static variable to use a react hook. Using the useState
hook will allow the <NoteCard/>
component to rerender each time we update the state, therefore allowing the position to be updated in our UI as well.
//let position = JSON.parse(note.position);
const [position, setPositon] = useState(JSON.parse(note.position));
To calculate the distance and direction moved, we will need to set the starting position from when the mouse is clicked, as well as a reference to the card itself so we can determine the final x
& y
position of the card.
let mouseStartPos = { x: 0, y: 0 };
const cardRef = useRef(null);
Set card ref: ref={cardRef}
- This is the parent div in our NoteCard
component.
Mouse Down Event
The first action taken to drag a note is the mousedown
event, as a user clicks down on the header of our note card component.
On mouse down, we want to:
- Capture the starting
x
&y
position of the mouse by referencinge.clientX
&e.clientY
. - Add an event listener to the DOM that listens for the following
mousemove
events.
Note: We will create the
mouseMove
method in the next step.
const mouseDown = (e) => {
mouseStartPos.x = e.clientX;
mouseStartPos.y = e.clientY;
document.addEventListener("mousemove", mouseMove);
};
Mouse move event
The mouse move event will capture EVERY movement of the users mouse, and will be responsible for updating our card position.
const mouseMove = (e) => {
//1 - Calculate move direction
let mouseMoveDir = {
x: mouseStartPos.x - e.clientX,
y: mouseStartPos.y - e.clientY,
};
//2 - Update start position for next move.
mouseStartPos.x = e.clientX;
mouseStartPos.y = e.clientY;
//3 - Update card top and left position.
setPositon({
x: cardRef.current.offsetLeft - mouseMoveDir.x,
y: cardRef.current.offsetTop - mouseMoveDir.y,
});
};
The following will occur each time this method is called:
- We will capture the direction of the move by subtracting the starting position (set on
mouseDown
or in previousmouseMove
) by the current position of the mouse. This will give us a result that looks something like this:{x:0, y:-1}
. This states that the mouse moved 1 pixel up and zero pixels to the left. - Once the direction has been calculated, we reset
mouseStartPos
to the current position, in preperation for the next move. - Last, we set the official Note Card position by subtracting the cards offset Left & Top position by the move direction, therefore incrementally updating the cards position as we move it.
To test this out, let's add the mousedown
event to our NoteCard header.
<div className="card-header"
//....
onMouseDown = { mouseDown };
>
Mouse up
If you are building along and testing this out right away, you may have noticed that the card stays stuck to our mouse even after we try to release the Note card. Let's resolve this now.
This can be solved with the mouseup
event, which will simply remove the mousemove
& mouseup
from the DOM when we release the card.
const mouseUp = () => {
document.removeEventListener("mousemove", mouseMove);
document.removeEventListener("mouseup", mouseUp);
};
To use this, we must add the mouseup
event listener in our original mousedown
method.
const mouseDown = (e) => {
mouseStartPos.x = e.clientX;
mouseStartPos.y = e.clientY;
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);
};