17

I am working on a page in witch all its contents are scaled by using zoom. The problem is that when I drag something in the page the dragging item gets a bad position that seems relative to the zoom amount.

To solve this I tried to do some math on the position of the draggable component, but seems that even tho visually its corrected, the "true" position its not recalculated.

here is some code to explain better:

var zoom = Math.round((parseFloat($("body").css("zoom")) / 100)*10)/10;

var x = $(this).data('draggable').position;
$(this).data('draggable').position.left = Math.round(x.left/zoom);
$(this).data('draggable').position.top = Math.round(x.top/zoom);

Any help would be greatly appreciated

Manuel
  • 171
  • 1
  • 1
  • 4
  • For future reference - if someone would ever be encountering the same problem - a similar question + working answer can be found [here](https://stackoverflow.com/questions/8605439/jquery-draggable-div-with-zoom/8605589#8605589). – ptriek Dec 23 '11 at 17:40

5 Answers5

21

Took a lot of time and effort to fix this, but finally, I have a solution that works.

This solution works for both Firefox and IE. #canvas is the div that contains the draggable. Note that we have to make sure the elements stays inside the canvas manually.

This also works if if canvas has a different zoom level than the rest of the page.

var pointerX;
var pointerY;
$(c).draggable({
  start : function(evt, ui) {
    pointerY = (evt.pageY - $('#canvas').offset().top) / zoom - parseInt($(evt.target).css('top'));
    pointerX = (evt.pageX - $('#canvas').offset().left) / zoom - parseInt($(evt.target).css('left'));
  },
  drag : function(evt, ui) {
    var canvasTop = $('#canvas').offset().top;
    var canvasLeft = $('#canvas').offset().left;
    var canvasHeight = $('#canvas').height();
    var canvasWidth = $('#canvas').width();

    // Fix for zoom
    ui.position.top = Math.round((evt.pageY - canvasTop) / zoom - pointerY); 
    ui.position.left = Math.round((evt.pageX - canvasLeft) / zoom - 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);
  }
});
torke1
  • 351
  • 2
  • 9
4
var zoom = $('#canvas').css('zoom');    
$('#dragme').draggable({
        drag: function(evt,ui)
        {
             var factor = (1 / zoom) -1);

             ui.position.top += Math.round((ui.position.top - ui.originalPosition.top) * factor);
             ui.position.left += Math.round((ui.position.left- ui.originalPosition.left) * factor);    
        }                 
    });
Yusuf Demirag
  • 773
  • 7
  • 10
1

If CSS Zoom is performed specifically on the html body, there is a very easy fix, but it does require you to modify the source. That's a no-go for some, but I'll post the solution here anyway.

So the draggable functionality of jQuery UI figures out the mouseposition using a method called _generatePosition. The problem is that it doesn't take zoom into account (duh), so we merely have to divide its outcome by the zoom level and everything will work.

So turn this:

    return {
        top: (

            // The absolute mouse position
            pageY -

            // Click offset (relative to the element)
            this.offset.click.top -

            // Only for relative positioned nodes: Relative offset from element to offset parent
            this.offset.relative.top -

            // The offsetParent's offset without borders (offset + border)
            this.offset.parent.top +
            ( this.cssPosition === "fixed" ?
                -this.offset.scroll.top :
                ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
        )/ ,
        left: (

            // The absolute mouse position
            pageX -

            // Click offset (relative to the element)
            this.offset.click.left -

            // Only for relative positioned nodes: Relative offset from element to offset parent
            this.offset.relative.left -

            // The offsetParent's offset without borders (offset + border)
            this.offset.parent.left +
            ( this.cssPosition === "fixed" ?
                -this.offset.scroll.left :
                ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
        )
    };

in

    var zoomLevel = parseFloat($('body').css("zoom") || "1.0");

    return {
        top: (

            // The absolute mouse position
            pageY -

            // Click offset (relative to the element)
            this.offset.click.top -

            // Only for relative positioned nodes: Relative offset from element to offset parent
            this.offset.relative.top -

            // The offsetParent's offset without borders (offset + border)
            this.offset.parent.top +
            ( this.cssPosition === "fixed" ?
                -this.offset.scroll.top :
                ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
        ) / zoomLevel,
        left: (

            // The absolute mouse position
            pageX -

            // Click offset (relative to the element)
            this.offset.click.left -

            // Only for relative positioned nodes: Relative offset from element to offset parent
            this.offset.relative.left -

            // The offsetParent's offset without borders (offset + border)
            this.offset.parent.left +
            ( this.cssPosition === "fixed" ?
                -this.offset.scroll.left :
                ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
        ) / zoomLevel
    };

That's all. I suggest saving it under a specific name so it's easily recognized that it has been modified. E.g.: jquery-ui.ZOOMFIX.js

I used the jquery-ui.js file from the following package: https://jqueryui.com/resources/download/jquery-ui-1.12.1.zip

kragovip
  • 426
  • 5
  • 11
  • How this answer relates to the question? – sg7 Mar 10 '18 at 13:31
  • 1
    @sg7 OP is asking for a solution for badly positioned dragging with jQuery UI. My answer provides a solution to fix badly positioned dragging with jQuery UI. Synce my answer answers the question I believe it to be sufficiently relevant. – kragovip Mar 10 '18 at 19:40
0

I was struggling with this for some hours. I had a grid where I started of with scaling it by setting the font-size (125% etc on the container). This worked well until I came into images that didn't scale with the grid.

Thought I'd use zoom instead and it worked brilliantly. Then I relized by draggable/droppable wasn't scaling with it hmm.. Spend several hours trying to destroy and re-init the jqueryUI objects but nothing worked.

Then finally I realized I could use a combination of font-size for zooming the grid and css-zoom for the images. Now everything works and I can drag/drop with the same instance of jQueryUI.

Hope this approach helps someone else. :-)

Stefan Lindberg
  • 553
  • 5
  • 10
0

Are you taking the scroll position, margin and padding into account? Such as:

x.left + 
parseInt($(this).css('margin-left')) + 
parseInt($(this).css('padding-left')) + 
parseInt($(this).parent().scrollLeft());

x.top + 
parseInt($(this).css('margin-top')) + 
parseInt($(this).css('padding-top')) + 
parseInt($(this).parent().scrollTop());

Then adjusting for the zoom value as needed?

Matthew Callis
  • 88
  • 1
  • 3
  • 5