If you're looking for a JavaScript drag and drop library that provides snapping to grids, or need to make sure items can only be dropped in a specific zone, interact.js provides these capabilities out of the box!
You may come across a scenario where you need an item to be cloned when you start dragging it - e.g. if you are building a map creator that lets you drop multiple of the same icon onto it.
Check out the code below, or you can also view a working Codepen.
// This stores the position of the current item being dragged
const position = { x: 0, y: 0 };
interact(".item")
.draggable({
// By setting manualStart to true - we control the manualStart.
// We need to do this so that we can clone the object before we begin dragging it.
manualStart: true,
listeners: {
move(event) {
position.x += event.dx;
position.y += event.dy;
event.target.style.transform = `translate(${position.x}px, ${position.y}px)`;
}
}
})
// This only gets called when we trigger it below using interact.start(...)
.on("move", function(event) {
const { currentTarget, interaction } = event;
let element = currentTarget;
// If we are dragging an item from the sidebar, its transform value will be ''
// We need to clone it, and then start moving the clone
if (
interaction.pointerIsDown &&
!interaction.interacting() &&
currentTarget.style.transform === ""
) {
element = currentTarget.cloneNode(true);
// Add absolute positioning so that cloned object lives
// right on top of the original object
element.style.position = "absolute";
element.style.left = 0;
element.style.top = 0;
// Add the cloned object to the document
const container = document.querySelector(".container");
container && container.appendChild(element);
const { offsetTop, offsetLeft } = currentTarget;
position.x = offsetLeft;
position.y = offsetTop;
// If we are moving an already existing item, we need to make sure
// the position object has the correct values before we start dragging it
} else if (interaction.pointerIsDown && !interaction.interacting()) {
const regex = /translate\(([\d]+)px, ([\d]+)px\)/i;
const transform = regex.exec(currentTarget.style.transform);
if (transform && transform.length > 1) {
position.x = Number(transform[1]);
position.y = Number(transform[2]);
}
}
// Start the drag event
interaction.start({ name: "drag" }, event.interactable, element);
});