Search code examples
javascriptcsszoomingscaledraggable

Dragging element not maintaining position relative to cursor when zooming body (using scale)


I want to achieve 2 things on my page:

  1. drag HTML elements on the screen;

  2. zoom the page using the "wheel" event.

I can accomplish both of these as follows:

Element Drag:

function make_element_draggable(id) {
    const elem = document.getElementById(id);
    elem.style.position = "absolute";
    let initX, initY, firstX, firstY, whichDown;
    window.addEventListener("mouseup", function() {
        if(whichDown) {
            whichDown.style.zIndex = 0;
        }
        whichDown = null;
    }, false);
    window.addEventListener("mousemove", draggable, false);
    elem.addEventListener("mousedown", function(e) {
        e.preventDefault();
        whichDown = this;
        initX = this.offsetLeft;
        initY = this.offsetTop;
        firstX = e.pageX;
        firstY = e.pageY;
    });

    function draggable(e) {
        e.preventDefault();
        if(!whichDown) return;
        whichDown.style.zIndex = 9;
        whichDown.style.left = initX + e.pageX - firstX + "px";
        whichDown.style.top = initY + e.pageY - firstY + "px";
    }
}

Page Zoom:

function page_zoom(container_id) {
    zoom = 1;
    zoom_speed = 0.1;
    const container = document.getElementById(container_id);
    document.addEventListener("wheel", function(e) {
        if(e.deltaY > 0) {
            container.style.transform = `scale(${zoom += zoom_speed})`;
        } else {
            container.style.transform = `scale(${zoom -= zoom_speed})`;
        }
    });
}

HTML:

<body id="body">
    <div id="container">
        <a id="text_1">TEXT 1</a>
    </div>
</body>

Usage:

make_element_draggable("text_1")
page_zoom("body")

Here is the result.

enter image description here

Notice how the drag works perfectly when no zoom in enabled (text remains centered in the circle), but once zoom in enabled the text no longer maintains its position relative to the cursor.

Is there a way to compensate for zoom amount, perhaps using it as a factor to adjust the top and left settings of the drag function?

Here is a CODEPEN showing all the code. Drag the text element, then zoom using your mouse wheel (trackpad on Mac), and notice the incorrect positioning of the text element relative to the cursor.


Solution

  • You forgot to take in account zoom when dragging:

    function make_element_draggable(id) {
        const elem = document.getElementById(id);
        elem.style.position = "absolute";
        let initX, initY, firstX, firstY, whichDown;
        window.addEventListener("mouseup", function() {
            if(whichDown) {
                whichDown.style.zIndex = 0;
            }
            whichDown = null;
        }, false);
        window.addEventListener("mousemove", draggable, false);
        elem.addEventListener("mousedown", function(e) {
            e.preventDefault();
            whichDown = this;
            initX = this.offsetLeft;
            initY = this.offsetTop;
            firstX = e.pageX;
            firstY = e.pageY;
        });
    
        function draggable(e) {
            e.preventDefault();
            if(!whichDown) return;
            whichDown.style.zIndex = 9;
            whichDown.style.left = initX + (e.pageX - firstX)/zoom + "px";
            whichDown.style.top = initY + (e.pageY - firstY)/zoom + "px";
        }
    }
    
    
    
    function page_zoom(container_id) {
        zoom = 1;
        zoom_speed = 0.1;
        const container = document.getElementById(container_id);
        document.addEventListener("wheel", function(e) {
            if(e.deltaY > 0) {
                container.style.transform = `scale(${zoom += zoom_speed})`;
            } else {
                container.style.transform = `scale(${zoom -= zoom_speed})`;
            }
        });
    }
    
    
    page_zoom("body")
    make_element_draggable("text_1")
    <body id="body">
        <div id="container">
            <a id="text_1">TEXT 1</a>
        </div>
    </body>