nigel
nigel

Reputation: 71

jquery-ui drag operation on zoomed container jumps on first mouse move

I create a container with two small child divs. I make the container and child divs draggable. The child divs drag as expected. The outer container drags as expected when the zoom ratio is 1.0 but when zoomed in, the drag operations jump when the container is first moved. There are some other related questions and possible answers but those I've tried only fix issues with the child containers not the parent container.

In particular, I've tried all of the suggestions here: jQuery Drag/Resize with CSS Transform Scale

And I reviewed this: jQuery UI Draggable jumps at the first drag which has no useful answer.

jsfiddle here: http://jsfiddle.net/nigelt/w521gv0j/5/

HTML:

<!-- Create an area where we will allow items to be moved -->
<div id=mycontainer>Press '+' to zoom in, '-' to zoom out, 'r' to reset.
    <br>This pane is draggable
    <!-- create a couple of boxes -->
    <div class="mybox" id="source">Draggable</div>
    <div class="mybox" id="dest">Draggable</div>
</div>

CSS:

#mycontainer {
    width: 600px;
    height: 400px;
    background: #ddd;
    position: absolute;
    top: 0;
    left: 0;
    float: none;
    border: solid 1px;
}
.mybox {
    position: absolute;
    background: #fff;
    border: 1px solid;
    border-radius: 6px;
    width: 100px;
    height: 75px;
    float: none;
    text-align: center;
}
#source {
    top: 50px;
    left: 100px;
}
#dest {
    top: 100px;
    left: 250px;
}
body {
    margin: 0px;
    /* force mycontainer to edge */
    overflow: hidden;
    /* no scrolling of body area. Clip the contents if moved */
}

javascript:

// global to set the zoomscale
zoomScale = 1;

// Set the zoom scale
var setZoomScale = function (zoom) {
    zoomScale = zoom;

    var container = $("#mycontainer");
    var scaleval = 'scale(' + zoom + ',' + zoom + ')';
    container.css('-ms-transform', scaleval);
    container.css('-webkit-transform', scaleval);
    container.css('transform', scaleval);
}

// Make the boxes draggable
// apply the stackoverflow fix
$("#source, #dest").draggable({
    start: function (event, ui) {
        ui.position.left = 0;
        ui.position.top = 0;
    },
    drag: function (event, ui) {
        // drag handle fix
        var changeLeft = ui.position.left - ui.originalPosition.left; // find change in left
        var newLeft = ui.originalPosition.left + changeLeft / ((zoomScale)); // adjust new left by our zoomScale

        var changeTop = ui.position.top - ui.originalPosition.top; // find change in top
        var newTop = ui.originalPosition.top + changeTop / zoomScale; // adjust new top by our zoomScale

        ui.position.left = newLeft;
        ui.position.top = newTop;

    }
});

// make the virtual window draggable
// apply the stackoverflow fix
$("#mycontainer").draggable({
    start: function (event, ui) {
        ui.position.left = 0;
        ui.position.top = 0;
    },
    drag: function (event, ui) {
        // drag handle fix
        var changeLeft = ui.position.left - ui.originalPosition.left; // find change in left
        var newLeft = ui.originalPosition.left + changeLeft / ((zoomScale)); // adjust new left by our zoomScale

        var changeTop = ui.position.top - ui.originalPosition.top; // find change in top
        var newTop = ui.originalPosition.top + changeTop / zoomScale; // adjust new top by our zoomScale

        ui.position.left = newLeft;
        ui.position.top = newTop;
    },
    stop: function (event, ui) {}
});

// add a keypress handler so we can change the zoom
$(document).keypress(function (ev) {
    var key = String.fromCharCode(ev.charCode);
    if (key == '+') {
        setZoomScale(zoomScale *= 1.5);
    } else if (key == '-') {
        setZoomScale(zoomScale /= 1.5);
    } else if (key == 'r') {
        // reset
        setZoomScale(1.0);
        $("#mycontainer").css({
            "top": 0,
                "left": 0
        });
    }
});

setZoomScale(1.5);

In the fiddle, the zoom ratio is initially set to 1.5. To reset it to 1.0, set focus to the demo window and press 'r'. After that the container drag works correctly

Upvotes: 0

Views: 2733

Answers (2)

Jeff
Jeff

Reputation: 99

This was my solution:

var highestTopHeight = 10;
var leftestLeftWidth = 10;
function resizeFix(event, ui) {
    let curWidth = ui.position.left + ui.size.width;
    if (curWidth > ui.element.parent().width() || curWidth === ui.element.parent().width()) {
        ui.size.width = ui.element.parent().width() - ui.position.left;
    }

    let curHeight = ui.position.top + ui.size.height;
    if (curHeight > ui.element.parent().height() || curWidth === ui.element.parent().height()) {
        ui.size.height = ui.element.parent().height() - ui.position.top;
    }

    if (ui.position.top >= 0)
        highestTopHeight = ui.size.height;

    if (ui.position.top < 0) {
        ui.position.top = 0;
        ui.size.height = highestTopHeight;
    }

    if (ui.position.left >= 0)
        leftestLeftWidth = ui.size.width;

    if (ui.position.left < 0) {
        ui.position.left = 0;
        ui.size.width = leftestLeftWidth;
    }
}

Apply that to the resize of the resizable:

$('#yourID').resizable({ ... resize: resizeFix, ... })

resize: resizeFix,

Upvotes: 0

nigel
nigel

Reputation: 71

Well, in the end it took more time to create the fiddle and write up the question than to find the answer. This post: Jquery draggable with zoom problem fixed my problem perfectly.

Modified code for my fiddle:

var pointerX;
var pointerY;
$("#mycontainer").draggable({
    start: function(evt, ui) {
        pointerY = (evt.pageY - $('body').offset().top) / zoomScale - parseInt($(evt.target).css('top'));
        pointerX = (evt.pageX - $('body').offset().left) / zoomScale - parseInt($(evt.target).css('left'));
        //console.log('pointer: ' + pointerX + ',' + pointerY);
    },
    drag: function(evt,ui) {
        var canvasTop = $('body').offset().top;
        var canvasLeft = $('body').offset().left;
        var canvasHeight = $('body').height();
        var canvasWidth = $('body').width();
        //console.log('canvas: ' + canvasLeft + ',' + canvasTop + '  ' + canvasWidth + 'x' + canvasHeight);

        // Fix for zoom
        ui.position.top = Math.round((evt.pageY - canvasTop) / zoomScale - pointerY); 
        ui.position.left = Math.round((evt.pageX - canvasLeft) / zoomScale - pointerX); 

        // Check if element is outside canvas
        if (ui.position.left < 0) ui.position.left = 0;
        //if (ui.position.left + $(this).width() > canvasWidth) ui.position.left = canvasWidth - $(this).width();  
        if (ui.position.top < 0) ui.position.top = 0;
        //if (ui.position.top + $(this).height() > canvasHeight) ui.position.top = canvasHeight - $(this).height();  

        // Finally, make sure offset aligns with position
        ui.offset.top = Math.round(ui.position.top + canvasTop);
        ui.offset.left = Math.round(ui.position.left + canvasLeft);
    },
    stop: function(event, ui) {
    }
});

Upvotes: 2

Related Questions