4

Basically what I want to achieve is to drag some div within another container div. Inner div should stop when it reaches the boundaries of the container div. I managed to do the most parts. But the problem is that if I drag inner div slowly & carefully it stops where it is supposed to be on the edges, however if I drag it faster, it sometimes overflow the edges or stops way before the edges. What is the problem here? What should I do to fix this?

Here is full code

function mouseMove(e) {
    e.preventDefault();    
    console.log("mouse moving");     

    if(isMouseDown > 0 && currentElement) {   
        mouseX = e.clientX;        
        mouseY = e.clientY;

        var diffX = mouseX - lastMouseX;
        var diffY = mouseY - lastMouseY;         

        if(diffX + lastElementX + elementWidth < rightLimit && diffX + lastElementX > leftLimit) {
            lastElementX += diffX;
            lastMouseX = mouseX;
        } 

        if(diffY + lastElementY + elementHeight < bottomLimit && diffY + lastElementY > topLimit) {
            lastElementY += diffY;
            lastMouseY = mouseY;                
        }                           

        setCurrentElementPosition(lastElementX, lastElementY);                              
    }                                                         
}

Above code runs on mouse move event changing inner div's position.

2 Answers 2

3

Here's my solution for you, I've programmed video games before, and you run into this problem a lot.

You're checking if it'll go out, but not doing anything if it does! If it goes outside, you have to set it to the edge.

https://jsfiddle.net/7btv7oqy/3/

var lastElementX = 0; //last X position of element
var lastElementY = 0; //last Y position of element
var lastMouseX = 0; //last X position of mouse
var lastMouseY = 0;//last Y position of mouse
var mouseX = 0; //current mouse position X
var mouseY = 0; //current mouse position Y

var currentElement = null; //currently selected div
var elementWidth = 0;
var elementHeight = 0;

var container = null; //container div
var isMouseDown = 0; //if greater than zero, mouse is down

//limits of container div
var bottomLimit = 0;
var topLimit = 0;
var leftLimit = 0;
var rightLimit = 0;

function init() {
    container = document.getElementsByClassName("container")[0];

    topLimit = container.getBoundingClientRect().top;
    bottomLimit = container.getBoundingClientRect().bottom;
    leftLimit = container.getBoundingClientRect().left;
    rightLimit = container.getBoundingClientRect().right;

    document.addEventListener("mousedown", function mouseDown(e) {
        e.preventDefault();
        ++isMouseDown;
        document.addEventListener("mousemove", mouseMove);

        setCurrentElement(getElementUnderMouse(e)); //set current element
        currentElement.style.position = "absolute";

        lastElementX = currentElement.getBoundingClientRect().left;
        lastElementY = currentElement.getBoundingClientRect().top;

        lastMouseX = e.clientX;
        lastMouseY = e.clientY;


    });

    document.addEventListener("mouseup", function mouseUp(e) {
        e.preventDefault();
        --isMouseDown;
        setCurrentElement(null);
        document.removeEventListener("mousemove", mouseMove);
    });

}

function mouseMove(e) {
    e.preventDefault();
    console.log("mouse moving");
    // report(e);

    if(isMouseDown > 0 && currentElement) {
        mouseX = e.clientX;
        mouseY = e.clientY;

        var diffX = mouseX - lastMouseX;
        var diffY = mouseY - lastMouseY;

        if(diffX + lastElementX + elementWidth < rightLimit && diffX + lastElementX > leftLimit) {
            lastElementX += diffX;
        } else {
            //without this, the location wouldn't update at all:

            //check if it would go off the right edge, set to right edge
            if (diffX + lastElementX + elementWidth >= rightLimit) {
                lastElementX = rightLimit - elementWidth;
            }

            //check if it would go off the left edge, set to left edge
            if(diffX + lastElementX <= leftLimit) {
                lastElementX = leftLimit;
            }

        }
        //this will always happen:
        lastMouseX = mouseX;

        if(diffY + lastElementY + elementHeight < bottomLimit && diffY + lastElementY > topLimit) {
            lastElementY += diffY;

        } else {
            //without this, the location wouldn't update at all:

            //check if it would go off the bottom edge, set to bottom edge
            if(diffY + lastElementY + elementHeight >= bottomLimit) {
                lastElementY = bottomLimit - elementHeight;
            }


            //check if it would go off the top edge, set to top edge
            if(diffY + lastElementY <= topLimit) {
                lastElementY = topLimit;
            }

        }

        //this will always happen:
        lastMouseY = mouseY;

        setCurrentElementPosition(lastElementX, lastElementY);
    }
}

function setCurrentElementPosition(left = null, top = null) {
    if(currentElement) {
        currentElement.style.top = top + 'px'
        currentElement.style.left = left + 'px';
    }
}

function setCurrentElement(element) {
    currentElement = element;
    if(element) {
        elementWidth = currentElement.offsetWidth;
        elementHeight = currentElement.offsetHeight;
    } else {
        elementWidth = 0;
        elementHeight = 0;
    }
}

function hasClass(element, cls) {
    return ("" + element.className + "").indexOf("" + cls + "") > -1;
}

function getElementUnderMouse(e) {
    var x = e.clientX;
    var y = e.clientY;
    return document.elementFromPoint(x, y);
}

function report(e) {
    console.log("isMouseDown: " + isMouseDown);
    console.log("mouseX: " + e.clientX);
    console.log("mouseY: " + e.clientY);
    console.log("currentElement: " + currentElement);
    console.log("currentElement top: " + currentElement.getBoundingClientRect().top);
    console.log("currentElement bottom: " + currentElement.getBoundingClientRect().bottom);
    console.log("container top: " + container.getBoundingClientRect().top);
    console.log("container bottom: " + container.getBoundingClientRect().bottom);
}

init();

EDIT: Not sure why it's missing by one pixel at the right and bottom, you'll have to do some investigation on that front. Seems like it's not taking into consideration the border, despite you using offsetWidth. Good luck!

Sign up to request clarification or add additional context in comments.

Comments

2

Here's my take; I've decoupled the element's movement from diffX/diffY:

https://jsfiddle.net/ttyrtjez/

// in onmousedown
offsetX = e.clientX - lastElementX;
offsetY = e.clientY - lastElementY;

// in onmousemove
var newElementX = mouseX - offsetX;
var newElementY = mouseY - offsetY;

if (newElementX + elementWidth >= rightLimit - 1) newElementX = rightLimit - elementWidth - 2;
if (newElementX < leftLimit) newElementX = leftLimit;
if (newElementY + elementHeight >= bottomLimit - 1) newElementY = bottomLimit - elementHeight - 2;
if (newElementY < topLimit) newElementY = topLimit;

setCurrentElementPosition(newElementX, newElementY);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.