dnd-kit icon indicating copy to clipboard operation
dnd-kit copied to clipboard

Snap to other DnD objects or array of custom coordinates?

Open pepsiamir opened this issue 2 years ago • 2 comments

Imagine I want to drag a box and snap it another dnd box with-in a specific pixel range. Is there any way to do it? In documentation there's only a solution for Snap to grid but not Snap to custom points

pepsiamir avatar Nov 04 '23 14:11 pepsiamir

To achieve snapping of draggable items within a certain pixel range, you will need to implement a custom solution. dnd-kit provides a set of built-in modifiers which you can find in their repository here: https://github.com/clauderic/dnd-kit/tree/master/packages/modifiers/src

While dnd-kit doesn't offer a direct solution for snapping to custom points, you can refer to a snapping mechanism I've implemented in a previous project: https://github.com/pixelass/react-mops/blob/master/packages/react-mops/src/guides/snapping.ts#L89 Although the code might appear complex, due to additional functionalities like resizing and rotation, the core concept is straightforward:

  • Compare the position of the dragged item with the positions of other items.
  • Determine if the items should snap based on certain conditions, such as proximity within a threshold or sides and/or center

Here's a simplified function to illustrate the snapping logic on the x or y-axis:

function inRange(value1: number, value2: number, threshold = 10): boolean {
	return value1 <= value2 + threshold && value1 >= value2 - threshold;
}
const snap = {
	x: siblings.find(sibling => inRange(dragging.x, sibling.x)),
	y: siblings.find(sibling => inRange(dragging.y, sibling.y))
}

This function checks if the dragged ttem is within a 10-pixel range of any sibling on both the x and y axes. You can customize the snapping behavior by defining different rules for snapping, such as center-to-center or edge-to-edge alignment.

Note: Keep in mind that the sample code above is just a simplified logic that does not look for the closest item but rather the first match in the collection. It also doesn't work as it is just an illustration of the basic mechanism I hope this helps. (see example in my comment below)

pixelass avatar Nov 10 '23 04:11 pixelass

Here's an example to snap the dragged item to the container's center points

export function snapToCenter({
	containerNodeRect,
	draggingNodeRect,
	transform,
}: ModifierArguments) {
	if (!draggingNodeRect || !containerNodeRect) {
		return transform;
	}

	const dragging = {
		x: draggingNodeRect.left + draggingNodeRect.width / 2,
		y: draggingNodeRect.top + draggingNodeRect.height / 2,
	};
	const container = {
		x: containerNodeRect.left + containerNodeRect.width / 2,
		y: containerNodeRect.top + containerNodeRect.height / 2,
	};
	return {
		...transform,
		x: inRange(dragging.x + transform.x, container.x) ? container.x - dragging.x : transform.x,
		y: inRange(dragging.y + transform.y, container.y) ? container.y - dragging.y : transform.y,
	};
}

pixelass avatar Nov 10 '23 05:11 pixelass