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
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 found I could just wrap the scaled element with another div and set draggable on it instead.
I had the same problem, and the easiest way for me was NOT to use the jquery-ui's draggable functionality since it does not support CSS3 transform properties.
What worked for me really good was to implement an own draggable functionality: http://css-tricks.com/snippets/jquery/draggable-without-jquery-ui/
I know it does not solve your problem 100%, since you still want to use the jquery-ui draggable functionality, but it could help others.
I would add this as a comment to the accepted answer from Gung Foo but I don't have the rep to comment.
I found Gung's answered worked perfectly for me however the resizable fix only worked when dragging from the bottom-right corner. I also have handles on the other three corners and found the element would shift so I had to add the fix from draggable to the resizable functions.
Maybe there's a better way, or I missed something, but I found the modified resizable below worked for me from all handles:
$(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
// adjust the initial position to accomodate for the scale
start: function(event, ui){
ui.position.left = Math.round(ui.position.left/zoomScale);
ui.position.top = Math.round(ui.position.top/zoomScale);
ui.originalPosition.left = ui.position.left;
ui.originalPosition.top = ui.position.top;
},
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;
// if the position is changed by a NW,NE,SW handle resize adjust for the scale
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;
}
});
Another approach would be to add a plugin that compensate for the transformation ( remember to add "transform : true" to the plugin initialization.
The ui.draggable need to be passed via a inverse matrix of the transformation in order to position the element in the un-transformed space which the browser later transform on display.
For "draggable" the following worked for me ( jqueryui 1.10 ) ( the matrix calculation I have taken from jquery.panzoom):
var Matrix = function(a, b, c, d, e, f, g, h, i) {
if ($.type(a) === 'array') {
this.elements = [
+a[0], +a[2], +a[4],
+a[1], +a[3], +a[5],
0, 0, 1
];
} else {
this.elements = [
a, b, c,
d, e, f,
g || 0, h || 0, i || 1
];
}
};
Matrix.prototype = {
/**
* Multiply a 3x3 matrix by a similar matrix or a vector
* @param {Matrix|Vector} matrix
* @return {Matrix|Vector} Returns a vector if multiplying by a vector
*/
x: function(matrix) {
var isVector = matrix instanceof Vector;
var a = this.elements,
b = matrix.elements;
if (isVector && b.length === 3) {
// b is actually a vector
return new Vector(
a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
);
} else if (b.length === a.length) {
// b is a 3x3 matrix
return new Matrix(
a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
);
}
return false; // fail
},
/**
* Generates an inverse of the current matrix
* @returns {Matrix}
*/
inverse: function() {
var d = 1 / this.determinant(),
a = this.elements;
return new Matrix(
d * ( a[8] * a[4] - a[7] * a[5]),
d * (-(a[8] * a[1] - a[7] * a[2])),
d * ( a[5] * a[1] - a[4] * a[2]),
d * (-(a[8] * a[3] - a[6] * a[5])),
d * ( a[8] * a[0] - a[6] * a[2]),
d * (-(a[5] * a[0] - a[3] * a[2])),
d * ( a[7] * a[3] - a[6] * a[4]),
d * (-(a[7] * a[0] - a[6] * a[1])),
d * ( a[4] * a[0] - a[3] * a[1])
);
},
/**
* Calculates the determinant of the current matrix
* @returns {Number}
*/
determinant: function() {
var a = this.elements;
return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
}
};
var Vector = function (x, y, z) {
this.elements = [ x, y, z ];
};
/**
* Get the element at zero-indexed index i
* @param {Number} i
*/
Vector.prototype.e = Matrix.prototype.e = function(i) {
if( this.elements[ i ] != undefined ){
return this.elements[ i ];
}
return this.elements;
};
$.ui.plugin.add("draggable", "transform", {
start: function( event, ui ) {
if(!$(this).data('ui-draggable')){
return false;
}
var inst = $(this).data("ui-draggable");
inst.matrix = new Matrix(function(matrix){
var rmatrix = new RegExp(
'^matrix\\(' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\)$'
);
var matrix = rmatrix.exec( matrix );
if (matrix) {
matrix.shift();
}
return matrix || [ 1, 0, 0, 1, 0, 0 ];
}([$(this).parents('[style*="transform"]').css('transform')]));
},
drag: function( event, ui ) {
if(!$(this).data('ui-draggable')){
return false;
}
var inst = $(this).data("ui-draggable");
var t_pos = inst.matrix.inverse().x(new Vector(ui.position.left, ui.position.top, 0));
ui.position.left = t_pos.e(0);
ui.position.top = t_pos.e(1);
if(inst.options.grid) {
ui.position.left = ui.position.left - ui.position.left % inst.options.grid[0];
ui.position.top = ui.position.top - ui.position.top % inst.options.grid[1];
}
if( inst.containment ){
if( ui.position.left < inst.containment[0] ){
ui.position.left = inst.containment[0];
}
if( ui.position.left > inst.containment[2] ){
ui.position.left = inst.containment[2];
}
if( ui.position.top < inst.containment[1] ){
ui.position.top = inst.containment[1];
}
if( ui.position.top > inst.containment[3] ){
ui.position.top = inst.containment[3];
}
}
},
});
The top answer was working for me till i've found a glitch on draggable :(
When the containment itself is also scaled:
Luckily, i've found a solution here