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 s
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. :-)
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
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);
}
});
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);
}
});
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?