I recently started writing a post about snapping but realized I needed to write a post about dragging first.
In this post, I'm going to quickly cover how to drag a shape on a canvas, or in an application that uses some kind of zoom ui. While it may seem obvious, implementing dragging in the wrong way means that other features, such as snapping, will become much more difficult to implement later on. It's worth getting it right the first time!
👋 Just want to look at some code? Click here for the CodeSandbox.
Picking the Right Delta
When a user drags a shape, its our job to calculcate the shape's new position based on whatever browser event we've received. On the web, this is usually a mouse move, touch move, or pointer move; but it could also be a scroll, pan, or zoom event, too.
Many applications calculate this new position by finding the pointer's movement—the difference between the pointer's current point and its previous point)—and adding this to the shape's current position. While this works, it's a bad idea. :(
A far better idea is to calculate the shape's position by taking the pointer's delta—i.e. the difference between the pointer's current point and the point where the drag began. We can then add this delta to the shape's original position in order to find its new position.
Why is this better?
There are several advantages to this approach:
- You can implement a "dead zone" to prevent accidental drags.
- You can update the position while scrolling during the drag.
- You can restore a shape's position if the user cancels the drag.
- You can freely adjust the delta with features like snapping, precision mode, or elastic bounds.
In later posts, I'll show how you could build these types of features on top of this strategy.
Until then, you'll have to trust me: in every case, the only way to implement these features is to never rely on the shape's "current" position and always compare against the shape's original position instead.
Want to see this in practice? Here's an example in React.