I am applying a CSS transform (and the browser specific -webkit, -o etc):
transform: matrix(0.5 , 0 , 0, 0.5, 0 , 0);
to a div then using jQuery's draggable() and resizable() plugins on children of said div.
The problem I had was that when dragging or resizing the child elements, the alteration jQuery made were out of "sync" with the mouse by a factor equal to the scale applied.
I found a solution on stackoverflow (though I stupidly did not bookmark it and now cant find it....) that suggested patching the plugins, and it worked wonderfully. It went along these line:
function monkeyPatch_mouseStart() {   // don't really need this, but in case I did, I could store it and chain   // var oldFn = $.ui.draggable.prototype._mouseStart ;   $.ui.draggable.prototype._mouseStart = function(event) {      var o = this.options;      //Create and append the visible helper     this.helper = this._createHelper(event);      //Cache the helper size     this._cacheHelperProportions();      //If ddmanager is used for droppables, set the global draggable     if($.ui.ddmanager)       $.ui.ddmanager.current = this;      /*      * - Position generation -      * This block generates everything position related - it's the core of draggables.      */      //Cache the margins of the original element     this._cacheMargins();      //Store the helper's css position     this.cssPosition = this.helper.css("position");     this.scrollParent = this.helper.scrollParent();      //The element's absolute position on the page minus margins      //PATCH CODE     this.offset = this.positionAbs = getViewOffset(this.element[0]);     //END      this.offset = {       top: this.offset.top - this.margins.top,       left: this.offset.left - this.margins.left     };      $.extend(this.offset, {       click: { //Where the click happened, relative to the element         left: event.pageX - this.offset.left,         top: event.pageY - this.offset.top       },       parent: this._getParentOffset(),       relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper     });      //Generate the original position     this.originalPosition = this.position = this._generatePosition(event);     this.originalPageX = event.pageX;     this.originalPageY = event.pageY;      //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied     if(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)){     }      //Set a containment if given in the options     if(o.containment)       this._setContainment();      //Trigger event + callbacks     if(this._trigger("start", event) === false) {       this._clear();       return false;     }      //Recache the helper size     this._cacheHelperProportions();      //Prepare the droppable offsets     if ($.ui.ddmanager && !o.dropBehaviour)       $.ui.ddmanager.prepareOffsets(this, event);      this.helper.addClass("ui-draggable-dragging");     this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position      //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)     if ( $.ui.ddmanager && $.ui.ddmanager.dragStart) $.ui.ddmanager.dragStart(this, event);      return true;   }  }  function getViewOffset(node) {   var x = 0, y = 0, win = node.ownerDocument.defaultView || window;   if (node) addOffset(node);   return { left: x, top: y };    function getStyle(node) {     return node.currentStyle || // IE            win.getComputedStyle(node, '');   }    function addOffset(node) {     var p = node.offsetParent, style, X, Y;     x += parseInt(node.offsetLeft, 10) || 0;     y += parseInt(node.offsetTop, 10) || 0;      if (p) {       x -= parseInt(p.scrollLeft, 10) || 0;       y -= parseInt(p.scrollTop, 10) || 0;        if (p.nodeType == 1) {         var parentStyle = getStyle(p)           , localName   = p.localName           , parent      = node.parentNode;         if (parentStyle.position != 'static') {           x += parseInt(parentStyle.borderLeftWidth, 10) || 0;           y += parseInt(parentStyle.borderTopWidth, 10) || 0;            if (localName == 'TABLE') {             x += parseInt(parentStyle.paddingLeft, 10) || 0;             y += parseInt(parentStyle.paddingTop, 10) || 0;           }           else if (localName == 'BODY') {             style = getStyle(node);             x += parseInt(style.marginLeft, 10) || 0;             y += parseInt(style.marginTop, 10) || 0;           }         }         else if (localName == 'BODY') {           x += parseInt(parentStyle.borderLeftWidth, 10) || 0;           y += parseInt(parentStyle.borderTopWidth, 10) || 0;         }          while (p != parent) {           x -= parseInt(parent.scrollLeft, 10) || 0;           y -= parseInt(parent.scrollTop, 10) || 0;           parent = parent.parentNode;         }         addOffset(p);       }     }     else {       if (node.localName == 'BODY') {         style = getStyle(node);         x += parseInt(style.borderLeftWidth, 10) || 0;         y += parseInt(style.borderTopWidth, 10) || 0;          var htmlStyle = getStyle(node.parentNode);         x -= parseInt(htmlStyle.paddingLeft, 10) || 0;         y -= parseInt(htmlStyle.paddingTop, 10) || 0;       }        if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;       if ((Y = node.scrollTop))  y += parseInt(Y, 10) || 0;     }   } } var isNumber = function(value) {   return !isNaN(parseInt(value, 10)); };   I have made my own changes such as (you can see on the 6-7 lines the multiplication of the movement by a "scale factor"):
 $.ui.draggable.prototype._generatePosition = function(event) {     var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);     var pageX = event.pageX;     var pageY = event.pageY;     //PATCH CODE     if($(this.element[0]).hasClass('item')){         pageY = this.originalPageY + ((pageY - this.originalPageY)*(1/$.viewbox.foreground.scale));         pageX = this.originalPageX + ((pageX - this.originalPageX)*(1/$.viewbox.foreground.scale));     }     //END     /*      * - Position constraining -      * Constrain the position to a mix of grid, containment.      */      if(this.originalPosition) { //If we are not dragging yet, we won't check for options        if(this.containment) {         if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;         if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;         if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;         if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;       }        if(o.grid) {         var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];         pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;          var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];         pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;       }     }     return {       top: (         pageY                               // The absolute mouse position         - this.offset.click.top                         // Click offset (relative to the element)         - this.offset.relative.top                        // Only for relative positioned nodes: Relative offset from element to offset parent         - this.offset.parent.top                        // The offsetParent's offset without borders (offset + border)         + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))       ),       left: (         pageX                               // The absolute mouse position         - this.offset.click.left                        // Click offset (relative to the element)         - this.offset.relative.left                       // Only for relative positioned nodes: Relative offset from element to offset parent         - this.offset.parent.left                       // The offsetParent's offset without borders (offset + border)         + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))       )     };    }   So big thank you to whoever suggested that.
So, my question! Has anyone come across a nice way to have draggable/resizable events inside a scaled element that doesn't require patching jQuery? I have googled, and this was the best solution I could find. Does anyone know of alternative to jquery that perhaps works under these conditions, with CSS transforms?
Many thanks for any responses.
It's been a while since this question was asked. I have found (actually created) an answer. All it requires is setting callback handlers. No editing jquery-ui needed!
Note: zoomScale in this example is a global variable and the transform is set using animate (aided by jquery.transform.js) like so:
target.animate({     transform: 'scale(' + zoomScale + ')' });   Take a look at this:
transform scale() fix for resizable:
$(this).resizable({     minWidth: -(contentElem.width()) * 10,  // these need to be large and negative     minHeight: -(contentElem.height()) * 10, // so we can shrink our resizable while scaled     resize: function(event, ui) {          var changeWidth = ui.size.width - ui.originalSize.width; // find change in width         var newWidth = ui.originalSize.width + changeWidth / zoomScale; // adjust new width by our zoomScale          var changeHeight = ui.size.height - ui.originalSize.height; // find change in height         var newHeight = ui.originalSize.height + changeHeight / zoomScale; // adjust new height by our zoomScale          ui.size.width = newWidth;         ui.size.height = newHeight;      } });   transform scale() fix for draggable:
$(this).draggable({     handle: '.drag-handle',     start: function(event, ui) {         ui.position.left = 0;         ui.position.top = 0;     },     drag: function(event, ui) {          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;      } });   Let me know if you find any problems or further improvements on this. :)
Reference: jQuery-UI resizable/draggable with transform: scale() set
I was trying the transform scale() fix for resizable posted by gungfoo on a element displayed at 10% of its actual size and the method didin't work. The cursor still moved away from the element during resizing.
I changed the last two lines of the resizeFix method to directly update the element's width and height and this solved my issue.
function resizeFix(event, ui) {      var changeWidth = ui.size.width - ui.originalSize.width; // find change in width     var newWidth = ui.originalSize.width + changeWidth / zoomScale; // adjust new width by our zoomScale      var changeHeight = ui.size.height - ui.originalSize.height; // find change in height     var newHeight = ui.originalSize.height + changeHeight / zoomScale; // adjust new height by our zoomScale      ui.originalElement.width(newWidth);     ui.originalElement.height(newHeight); } 
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With