Smart Drag Using JS?

Tags: , ,



Would it be possible to stop an element from dragging once it reaches the edge of the screen? I have been trying for the past hour with no luck. Here is my code so far:

dragElement(document.getElementById("mp3Audio"));

function dragElement(elmnt) {
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // && elmnt.offsetTop - pos2 - Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0) >= 0
    // set the element's new position:
    if (!(elmnt.style.top - document.documentElement.clientHeight <= 0) && !(elmnt.style.left - document.documentElement.clientWidth <= 0) && !(elmnt.style.top >= document.documentElement.clientHeight) && !(elmnt.style.left >= document.documentElement.clientWidth)){
        elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
        elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
    }
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
audio{
  background: #D6D6D6;
  border-radius: 0px;
  border: none;
  width: 100%;
  height: 10%;
  font-variant-numeric: oldstyle-nums;
  -webkit-text-stroke: thin;
  color: #FFFFFF;
  transition: 0.5s;
}

audio::-webkit-media-controls-panel{
  background: #A1A1A1;
  border: none;
}
<div id="mp3Audio" style="background: #FFFFFF; position: fixed; border: 5px solid #800000; border-radius: 10px; overflow: hidden; left: 0px; top: 0px; height: 240px; width: 426px; bottom: 1em; right: 4em;">
<img src="graphics/HighResIcon.png" style="object-fit: contain;" height="90%" width="100%">
<audio type="audio/mpeg" controlsList='nodownload' controls><source src="songs/amhere.mp3"></source></audio>
</div>
<button id="playbutton">e</button>

I thought I could use the clientHeight and clientWidth, but so far I have been unsuccessful. Any help would be much appreciated.

Answer

I renamed var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

to

xPosDiff = 0, yPosDiff = 0, xPosOld = 0, yPosOld = 0; so it were easier for me to understand them. 🙂

getBoundingClientRect() is pretty calculation heavy but I think it’s needed for your problem. From that method, you can extract the top, right, bottom, left from the element that you want to drag, and then I just compared it to 0, window.innerWidth, window.innerHeight, 0, but I also added the new X and Y difference for the cursor to that comparison. So if I predict that the new movement will make the element to cross any of the boundries (top, right…), I won’t move the element.

I had to resize #mp3Audio so it would fit inside the snippet. I also added a dashed border to better showcase the boundries.

PS. The code from W3Schools had the wrong thinking when doing the calculations IMHO, so I changed that as well. They had xPosDiff = xPosOld - e.clientX, which seems wrong because then you need to subtract that value from the old position: elmnt.offsetTop - yPosDiff. It seems backwards, where I prefer to add the difference instead. DS,

dragElement(document.getElementById("mp3Audio"));

function dragElement(elmnt) {
  var xPosDiff = 0, yPosDiff = 0, xPosOld = 0, yPosOld = 0;
  
   /* ADDED */
  var elmntRect;
  var insideTop, insideBottom, insideLeft, insideRight;
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    posXOld = e.clientX;
    posYOld = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    xPosDiff = e.clientX - xPosOld;
    yPosDiff = e.clientY - yPosOld;
    xPosOld = e.clientX;
    yPosOld = e.clientY;

    /* ADDED */
    elementRect = elmnt.getBoundingClientRect();
    insideTop =    elementRect.top    + yPosDiff >= 0;
    insideBottom = elementRect.bottom + yPosDiff <  window.innerHeight;
    insideLeft =   elementRect.left   + xPosDiff >= 0;
    insideRight =  elementRect.right  + xPosDiff <  window.innerWidth;
    
    // set the element's new position:
    if (insideTop && insideBottom && insideLeft && insideRight) {
      elmnt.style.top = (elmnt.offsetTop + yPosDiff) + "px";
      elmnt.style.left = (elmnt.offsetLeft + xPosDiff) + "px";
    }    
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
html, body {
  margin: 0px;
  padding: 0px;
}

body {
  border: 2px dashed #000;
  height: 95vh;
}

audio{
  background: #D6D6D6;
  border-radius: 0px;
  border: none;
  width: 100%;
  height: 10%;
  font-variant-numeric: oldstyle-nums;
  -webkit-text-stroke: thin;
  color: #FFFFFF;
  transition: 0.5s;
}

audio::-webkit-media-controls-panel{
  background: #A1A1A1;
  border: none;
}

#mp3Audio {
  position: fixed;
  left: 0px;
  top: 0px;
  /* bottom: 1em; */
  /* right: 4em; */
  height: 120px;  /* 240px */
  width:  213px;  /* 426px */

  overflow: hidden;
  border: 5px solid #800000;
  border-radius: 10px;
  background: #FFFFFF;
  box-sizing: border-box; /* ADDED */
}
<div id="mp3Audio">
<img src="graphics/HighResIcon.png" style="object-fit: contain;" height="90%" width="100%">
<audio type="audio/mpeg" controlsList='nodownload' controls><source src="songs/amhere.mp3"></source></audio>
</div>


Source: stackoverflow