I'm using interact.js and Svelte 5 (version 5.0.0-next.200) to drag an element which initially sits inside a scrollable div and must be dropped outside of that div. To make this work I need to clone the element and then drag the clone, I guess there's no way around.
Now, in a former Vanilla-js project I successfully adapted the code from this stackoverflow-answer and everything worked fine. However, I can't make it work in Svelte.
Below is my code. I assume the error is in interaction.start({ name: 'drag' }, ev.interactable, clone);
, which fails to pass the drag-action on to the clone. Instead the original div is moved double the distance, which I cannot explain either.
Any help is warmly welcome. I'm struggling with this since days.
EDIT: solved the striked-through part above and commented one line of code.
<script> import interact from 'interactjs'; const dragElement = (ev) => { const el = ev.target; let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx; let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy; el.style.webkitTransform = el.style.transform = `translate(${x}px,${y}px)`; el.setAttribute('data-x', x); el.setAttribute('data-y', y); } const makeDraggable = (DRAG) => { const draggable = document.querySelector('.draggable'); interact(draggable) .draggable({ allowFrom: '*', inertia: false, autoScroll: false, modifiers: [ interact.modifiers.restrict({ restriction: '#outer', endOnly: true }) ], onstart: (ev) => { console.log('DRAG START of ', ev.target); }, onmove: (ev) => { console.log('DRAG MOVE of ', ev.target); dragElement(ev); }, onend: (ev) => { console.log('DRAG END of ', ev.target); DRAG.node.orig.style.opacity = 100; // show original again after drag } }) .on('move', (ev) => { const el = ev.currentTarget; const interaction = ev.interaction; console.log('ON.MOVE TRY of ', el); if ( interaction.pointerIsDown && // Only on active interaction interaction.interacting() // Prevent activation by swiping through element ) { // If ORIG then make a clone if ( DRAG.active == false && // Create only one clone el.getAttribute('isClone') == 'orig' // Prevent re-cloning clones ) { DRAG.active = true; console.log('CLONING START'); // Clone node and set its position let clone = el.cloneNode(true); clone.style.left = el.offsetLeft +'px'; clone.style.top = el.offsetTop +'px'; clone.style.position = 'absolute'; clone.setAttribute('isClone', 'clone'); clone.style.backgroundColor = 'grey'; // makeDraggable(DRAG); // EDIT: this line caused the double-distance dragging // Append Clone and start drag interaction document.querySelector('#outer').appendChild(clone); console.log('CLONE IS READY: ', clone); DRAG.node.orig = el; DRAG.node.clone = clone; DRAG.node.orig.style.opacity = 0; // hide original while dragging clone interaction.start({ name: 'drag' }, ev.interactable, clone); // if CLONE then drag // } else if (el.getAttribute('isClone') == 'clone') { // // console.log('MOVING THE CLONE START'); // // dragElement(ev); // } } } }); }; /////////////////////////////////////////////////////////////// // DRAG State let DRAG = $state({ active: false, node: { orig: undefined, clone: undefined } }); /////////////////////////////////////////////////////////////// // Handle drag const handleDrag = () => { makeDraggable(DRAG); };</script><div id="outer"><h1>outer</h1><div id="inner"><h1>inner (scrollable)</h1><div class="draggable" isClone="orig" use:handleDrag>Drag me</div></div></div><style> #outer { position: absolute; background-color: yellow; height: 95vh; width: 90vw; } #inner { position: absolute; height: 400px; width: 100%; background: lightblue; overflow-y: scroll; } .draggable { position: absolute; background-color: black; color: white; height: 500px; width: 150px; }</style>