I'm writing an application where I use interact.js for DND(drag and drop). Often the start of a DND interaction causes a change in the page; dropzones changing position, being deleted, and new ones being created.
Creation (main issue)
In the snippet below, you get to initiate new dropzones by hovering on the orange box with the draggable. But they don't activate until you drop the draggable and pick it up again.
// DRAGGABLE
const draggable = document.getElementById("draggable");
let x = 0;
let y = 0;
interact(draggable).draggable({
autoScroll: false,
onmove(e) {
x += e.dx;
y += e.dy;
// translate the element
draggable.style.transform = "translate(" + x + "px, " + y + "px)";
}
});
// HOVERZONE
const hoverzone = document.getElementById("hoverzone");
setupDropzone(hoverzone, addNewZone);
// ADDED ZONES
function addNewZone() {
// Make html element
const node = document.createElement("div");
node.classList.add("box", "zone");
const textNode = document.createTextNode("Hi!");
node.appendChild(textNode);
document.body.appendChild(node);
// Ininialize interactable
setupDropzone(node);
}
// HELPER
function setupDropzone(element, ondragenter = () => {}) {
interact(element).dropzone({
accept: "#draggable",
ondrop(e) {
element.classList.remove("drag-hover");
},
ondragenter(e) {
element.classList.add("drag-hover");
ondragenter();
},
ondragleave(e) {
element.classList.remove("drag-hover");
}
});
}
body {
display: flex;
}
.box {
width: 120px;
border-radius: 8px;
padding: 20px;
margin: 1rem;
background-color: peru;
color: white;
touch-action: none;
user-select: none;
box-sizing: border-box;
}
#draggable {
background-color: #29e;
z-index: 99;
}
.zone {
background-color: pink;
}
.drag-hover {
outline: 3px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.27/interact.min.js"></script>
<div class="box" id="hoverzone">
Hover draggable on me!
</div>
<div class="box" id="draggable">
Drag me!
</div>
At some point, I tried using event.interaction.start(...) in new dropzones (like in here) by storing the dragstart event of the draggable, but that just broke things and lead nowhere.
I also found no way to delay the DND process (rect calculation, firing dragactivate on dropzones, etc.) after dragstart, until the new zones initialize.
Do you have either a solution to this with interact.js, or a similar DND library that solves this issue and the issue below elegantly?
Movement (solved but worth mentioning)
I solved the issue of dropzones moving using interact.dynamicDrop(true);. This is unideal as it recalculates rectangles on every dragmove, while the actual movement happens rarely in my case. Is there a way to just trigger recalculation once? Doing something like below complicated my code in way that's not worth it and felt too hacky:
- Enable
dynamicDrop - Wait for
dragmove - Disable
dynamicDrop