2017-06-23 5 views
3

Ich versuche, per Drag & Drop zu verwenden, um Bilder von einem <div> zum anderen zu bewegen.Snapping Drag & Drop-Elemente in JavaScript

Derzeit kann ich die Bilder überall im Ziel <div> verschieben, aber was ich wirklich will, ist, dass die Bilder zusammenschnappen, wenn sie fallen gelassen werden. Idealerweise könnten sie auf jeder Seite zusammenschnappen (nicht nur zum Beispiel unten oder rechts).

Ich habe ein paar verschiedene Dinge ausprobiert (einschließlich der Verwendung von <canvas>), und es hat nicht funktioniert.

Dies ist, was ich bisher:

var clone; 
 
var offsetx = null; 
 
var offsety = null; 
 
var isClone = false; 
 

 
function allowDrop(ev) { 
 
    ev.preventDefault(); 
 
} 
 

 
function drag(ev) { 
 
    offsetx = ev.target.offsetLeft - event.clientX; 
 
    offsety = ev.target.offsetTop - event.clientY; 
 
    
 
    ev.dataTransfer.setData("text", ev.target.id); 
 
} 
 

 
function dropTrash(ev) { 
 
    ev.preventDefault(); 
 

 
    var data = ev.dataTransfer.getData("text"); 
 
    var remove = document.getElementById(data); 
 

 
    remove.parentNode.removeChild(remove); 
 
} 
 

 
function drop(ev) { 
 
    ev.preventDefault(); 
 
    
 
    var data = ev.dataTransfer.getData("text"); 
 
} 
 

 
function dropClone(ev) { 
 
    ev.preventDefault(); 
 
    
 
    var data = ev.dataTransfer.getData("text"); 
 
    var num = Math.random() * (1000 - 1) + 1; 
 
    
 
    isClone = true; 
 
    
 
    clone = document.getElementById(data).cloneNode(true); 
 
    clone.id = "newId" + num.toString(); 
 
    clone.style.position = "absolute"; 
 
    clone.style.left = (event.clientX+offsetx)+"px"; 
 
    clone.style.top = (event.clientY+offsety)+"px"; 
 

 
    ev.target.appendChild(clone); 
 
} \t
html, body { 
 
    height: 100%; 
 
    padding: 0; 
 
    margin: 0; 
 
} 
 

 
div { 
 
    width: 50%; 
 
    height: 50%; 
 
    float: left; 
 
} 
 

 
#div1 { 
 
    background: #DDD; 
 
} 
 

 
#div2 { 
 
    background: #AAA; 
 
} 
 

 
#div3 { 
 
    background: #777; 
 
} 
 

 
#div4 { 
 
    background: #444; 
 
} 
 

 
#imgDiv { 
 
    width: 611px; 
 
    height: 324px; 
 
    border: 5px solid #DDD; 
 
}
<div id="div1"> 
 
</div> 
 

 
<div id="div2"> 
 
</div> 
 

 
<div id="div3" ondrop="dropTrash(event)" ondragover="allowDrop(event)"> 
 
    <img id="drag1" src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Bartagame_fcm.jpg/1200px-Bartagame_fcm.jpg" draggable="true" ondragstart="drag(event)" width="105" height="105"> 
 
    <img id="drag2" src="http://www.earthtimes.org/newsimage/lizard_Ngo_Van_Tri_big_281.jpg" draggable="true" ondragstart="drag(event)" width="105" height="105"> \t 
 
</div> 
 

 
<div id="div4"> 
 
    <div align="center" id="imgDiv" ondrop="dropClone(event)" ondragover="allowDrop(event)"></div> 
 
</div>

Antwort

1

Wenn Sie ein Bild zu starten ziehen, müssen Sie die Position des Cursors speichern, bezogen auf die jeweilige Bild.

Es gibt mehrere Position Eigenschaften in der MouseEvent, die Ihnen helfen wird, das zu berechnen, aber wenn Browser-Unterstützung kein Problem ist, würde ich für MouseEvent.offsetX und MouseEvent.offsetY gehen. Aus der Dokumentation:

Die offsetX/offsetY schreibgeschützte Eigenschaft der Mouseevent-Schnittstelle, die in der zwischen diesem Ereignis des Mauszeigers Koordinate und die Padding Rand des Zielknotens X/Y Offset bereitstellt.

Also, auf dragstart, tun Sie gerade:

x = e.offsetX; 
y = e.offsetY; 

Dann, wenn Sie das Bild in Ihrem fallen, nennen wir es Leinwand (man beachte die Kursivschrift, da es kein <canvas> Element , aber jedes andere Element, das Sie als Drop-Zone verwenden, ein <div> in diesem Beispiel), müssen Sie auf diese Leinwand, um die Position des Cursors relativ wissen, so können Sie denken, Sie offsetX und offsetY wieder, und Sie befinden sich nutzen könnten teilweise richtig. Das würde Ihnen den erwarteten Wert geben, wenn Sie das Bild auf der Canvas selbst fallen lassen, aber möglicherweise sind andere Bilder bereits darin, und Sie können die aktuelle über die andere fallen lassen und erhalten die offsetX und offsetY relativ dazu eine stattdessen.

Was können Sie tun, ist zu verwenden MouseEvent.pageX und MouseEvent.pageY und von diesem Wert, subtrahieren die Position der (linke obere Ecke) dieser Leinwand Element, das Sie von HTMLElement.offsetLeft erhalten und HTMLElement.offsetTop:

e.pageX - imageCanvas.offsetLeft; 
e.pageY - imageCanvas.offsetTop; 

Damit wird die Position des Cursors relativ zum Leinwand Element erhalten.

Nun müssen Sie die x und y Werte subtrahieren, die Sie auf dragstart gespeichert, und das wird Ihnen die left und top Werte der oberen linken Ecke des gezogenen Bild, bezogen auf das Leinwand Element:

image.style.left = (e.pageX - imagesCanvas.offsetLeft - x) + 'px'; 
image.style.top = (e.pageY - imagesCanvas.offsetTop - y) + 'px'; 

Alles zusammen wird es wie folgt aussehen:

let x; 
 
let y; 
 
let currentTarget = null; 
 
let cloneElement = false; 
 

 
function startDrag(e, clone) { 
 
    const target = e.target; 
 
    
 
    if (target.tagName === 'IMG') { 
 
    x = e.offsetX; 
 
    y = e.offsetY; 
 
    currentTarget = target; 
 
    cloneElement = clone; 
 
    } 
 
} 
 

 
function cloneImage(e) { 
 
    startDrag(e, true); 
 
} 
 

 
function moveImage(e) { 
 
    startDrag(e, false); 
 
} 
 

 
function removeImage(e) { 
 
    if (!cloneElement) { 
 
    currentTarget.remove(); 
 
    } 
 
} 
 

 
function stickImage(e) { 
 
    const image = cloneElement ? currentTarget.cloneNode(true) : currentTarget; 
 
    
 
    imagesCanvas.appendChild(image); 
 
    
 
    // + 1 for the border 
 
    
 
    image.style.left = (e.pageX - imagesCanvas.offsetLeft - x + 1) + 'px'; 
 
    image.style.top = (e.pageY - imagesCanvas.offsetTop - y + 1) + 'px'; 
 
    
 
    currentTarget = null; 
 
} 
 

 
function allowDrag(e) { 
 
    e.preventDefault(); 
 
} 
 

 
// Bind event listeners: 
 

 
const imagesBarElement = document.getElementById('imagesBar'); 
 
const imagesCanvasElement = document.getElementById('imagesCanvas'); 
 

 
document.addEventListener('dragenter', allowDrag); 
 
document.addEventListener('dragover', allowDrag); 
 

 
imagesBarElement.addEventListener('dragstart', cloneImage); 
 
imagesBarElement.addEventListener('drop', removeImage); 
 

 
imagesCanvasElement.addEventListener('dragstart', moveImage); 
 
imagesCanvasElement.addEventListener('drop', stickImage);
body { 
 
    margin: 0; 
 
    font-size: 0; 
 
    display: flex; 
 
    flex-direction: column; 
 
    height: 100vh; 
 
    user-select: none; 
 
} 
 

 
img { 
 
    width: 100px; 
 
    height: 100px; 
 
} 
 

 
#imagesBar { 
 
    height: 100px; 
 
    border-bottom: 1px solid #CCC; 
 
    padding: 10px 0; 
 
} 
 

 
#imagesBar > img { 
 
    margin: 0 0 0 10px; 
 
} 
 

 
#imagesCanvas { 
 
    position: relative; 
 
    background: #EEE; 
 
    flex-grow: 1; 
 
    overflow: hidden; 
 
} 
 

 
#imagesCanvas > img { 
 
    position: absolute; 
 
}
<div id="imagesBar"> 
 
    <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Bartagame_fcm.jpg/1200px-Bartagame_fcm.jpg" draggable="true"> 
 
    <img src="http://www.earthtimes.org/newsimage/lizard_Ngo_Van_Tri_big_281.jpg" draggable="true"> \t 
 
</div> 
 

 
<div id="imagesCanvas"></div>

+0

Das hat bei mir überhaupt nicht funktioniert. :/ – Yikes

+0

@Yikes Welcher Teil funktioniert nicht genau? Meinst du mit _snap together_, dass die Bilder nur nebeneinander ausgerichtet sein sollen, anstatt sie frei in eine beliebige Position zu bewegen? – Danziger

+0

Nein, du hast genau verstanden, was ich wollte (und danke, dass du dir die Zeit genommen hast, all dies zu schreiben)! Aus welchem ​​Grund auch immer, als ich diesen Code ausprobierte, funktionierte nicht einmal ein normaler Drop. Die Bilder würden einfach auf ihre ursprüngliche Position zurückspringen. Ich konnte nicht einmal den Teil testen, der sie zusammenschnipste. – Yikes