← Back toDrag and drop in React

Use vertical and horizontal lines as guides for element positioning

Written byPhuoc Nguyen
Created
23 Nov, 2023
When you're moving an element around its container, it's helpful to use the vertical and horizontal lines shown at the center as a guide. These lines ensure that you're moving the element in a straight line, which is crucial for keeping everything aligned on the page. Plus, it prevents any accidental movements in the wrong direction, making design adjustments smoother and less frustrating.
Highlighting center lines is useful in many situations, such as photo editing applications that require precise adjustments to an image's position or size. By using center lines while editing, you can ensure your changes are accurate and symmetrical.
In this post, we'll dive into how to implement this functionality with React.

Checking if the element is moving towards the center

In a previous lesson, we learned how to keep a draggable element within its container. To achieve this, we first obtained the bounding rectangle of the element's parent container and its own bounding rectangle using `getBoundingClientRect()`. Then, we calculated the distance the mouse had moved since the last click.
ts
const parent = node.parentElement;
const parentRect = parent.getBoundingClientRect();
const eleRect = node.getBoundingClientRect();

// How far the mouse has been moved
let dx = e.clientX - startPos.x;
let dy = e.clientY - startPos.y;
Next, we calculate the maximum distance that our element can be moved in every direction. To do this, we subtract the element's width and height from those of its parent container. This step is crucial to ensure that our draggable element doesn't move beyond the boundaries of its parent container.
ts
const maxX = parentRect.width - eleRect.width;
const maxY = parentRect.height - eleRect.height;
To ensure that the draggable element stays within its designated boundaries, we make use of the `Math.max()` and `Math.min()` methods. These handy functions ensure that the element cannot exceed the boundaries of its parent container.
ts
// Constrain movement within bounds
dx = Math.max(0, Math.min(dx, maxX));
dy = Math.max(0, Math.min(dy, maxY));
Let's talk about how to tell if users are moving an element close to the center of the container. If they are, we'll highlight the vertical and horizontal lines in the center of the container. This helps users align their draggable element with other elements on the page.
To make this happen, we need to calculate the distance between the draggable element and the center lines of its parent container. Once we know that, we can decide whether or not to show the highlight.
ts
const centerX = parentRect.left + parentRect.width / 2;
const centerY = parentRect.top + parentRect.height / 2;
const highlightVertical = Math.abs(eleRect.left + eleRect.width / 2 - centerX) <= THRESHOLD;
const highlightHorizontal = Math.abs(eleRect.top + eleRect.height / 2 - centerY) <= THRESHOLD;

setHighlight({ highlightHorizontal, highlightVertical });
We've added a new section of code that calculates the center point of the parent container. We use `parentRect.left + parentRect.width / 2` and `parentRect.top + parentRect.height / 2` to achieve this.
After that, we check if the draggable element is within a certain distance to either the horizontal or vertical center line. In the sample code above, this distance is represented by the `THRESHOLD` constants.
Lastly, we use the `setHighlight` function to update and indicate which line should be highlighted. By default, this function contains an object with two properties that are set to `false`.
ts
const [{ highlightHorizontal, highlightVertical }, setHighlight] = React.useState({
highlightHorizontal: false,
highlightVertical: false,
});
We can include the highlight state in the returned value of the hook. This way, we can easily determine whether we should highlight the lines based on their corresponding state.
ts
const useDraggable = () => {
return [ref, dx, dy, highlightHorizontal, highlightVertical];
};

Creating horizontal and vertical lines

Instead of using real elements to represent horizontal and vertical lines, we can use the `::before` and `::after` pseudo-elements.
For instance, to position the horizontal line, we use the `::before` element on the container. This pseudo-element is commonly used to add decorative elements to a webpage. In our case, we're using it to create a horizontal line that spans the width of the container.
We set `content: ''` to indicate that no content should be inserted before the container's content. Then, we set `position: absolute` to ensure that our line is positioned relative to its parent container.
Next, we set `top: 50%` to position our line at 50% of its parent's height. We also set both `left` and `right` properties to 0, so that our line spans the entire width of its parent container. Finally, we set its height with `height: 2px`, and use `transform: translateY(-50%)` to adjust its vertical position so that it sits exactly in the center of its parent container.
css
.container--horizontal::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 2px;
transform: translateY(-50%);
}
With the same approach, creating a vertical line using the `::after` element is a breeze.
css
.container--vertical::after {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 2px;
transform: translateX(-50%);
}
To highlight specific lines, we can simply change their background color.
css
.container--horizontal::before,
.container--vertical::after {
background-color: rgb(99 102 241);
}
Last but not least, it's crucial to make sure that these lines don't cover the draggable element. One way to avoid this is by increasing the `z-index` property of the draggable element.
css
.draggable {
z-index: 1;
}
We've created two CSS classes that represent two different versions of the container. You can add these classes to the container when the corresponding highlight state property is `true`.
tsx
const [..., highlightHorizontal, highlightVertical] = useDraggable();

// Render
return (
<div
className={`container ${highlightHorizontal ? 'container--horizontal' : ''} ${highlightVertical ? 'container--vertical' : ''}`}
>
...
</div>
);
With these changes, our draggable element will now snap to the center lines of its container and highlight when it's nearby. This allows users to align their elements with greater ease and creates a more polished user experience overall.
Take a look at the demo below. You can drag the square towards the center of its container. The vertical and horizontal lines will appear as soon as the square is within a certain distance from the center of the container. In this demo, the distance threshold is set to 10 pixels.

Conclusion

We've made some improvements to enhance the user experience when dragging an element towards the center of its container. Thanks to a React hook, horizontal and vertical lines now appear, making it easier for users to align their elements with precision. This results in a more polished final product.
To keep our code clean and concise, we've implemented these changes with `::before` and `::after` pseudo-elements. This way, we can maintain our code with ease over time. Overall, this is an excellent example of how React hooks can help create powerful UI interactions with minimal effort.
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn