jQuery拖放/调整CSS变换比例

我正在应用CSS转换(和特定于浏览器的-webkit,-o等):

变换:matrix(0.5,0,0,0.5,0,0);

到一个div,然后使用jQuery的draggable()和resizable()插件的孩子说的div。

我遇到的问题是,当拖动或调整子元素的大小时,jQuery所做的更改与鼠标的“同步”超出了所应用的比例。

我find了一个解决scheme在stackoverflow(虽然我愚蠢没有书签,现在不能find它….),build议修补插件,它工作奇妙。 它沿着这条线:

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)); }; 

我做了自己的改变,比如(你可以在6-7行看到运动乘以“比例因子”):

  $.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() )) ) }; } 

非常感谢谁提出这个build议。

所以,我的问题! 有没有人遇到一个很好的方式来在不需要修补jQuery的缩放元素中放置可拖动/可resize的事件? 我GOOGLE了,这是我能find的最好的解决scheme。 有没有人知道替代jquery,也许在这些条件下工作,用CSS转换?

非常感谢任何回应。

自问这个问题以来,已经有一段时间了。 我发现(实际上是)创造了一个答案。 它只需要设置callback处理程序。 不需要编辑jquery-ui!

注意:这个例子中的zoomScale是一个全局variables,变换是使用animation(由jquery.transform.js辅助)设置的,如下所示:

 target.animate({ transform: 'scale(' + zoomScale + ')' }); 

看看这个:

变换scale()修复可resize:

 $(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; } }); 

变换scale()修复为可拖动:

 $(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; } }); 

如果您发现任何问题或进一步改进,请告知我们。 🙂

参考: jQuery-UI可resize/拖放与transform:scale()设置

我自己的“答案”就是在这个场合下,为了适应jQuery UI的拖动,做一个单独的交互,叫做“traggable”。

https://github.com/paullth/JS-libs

http://jsfiddle.net/paul_herve/ws27C/4/

仍然想听听任何替代scheme…

另一种方法是添加一个补偿转换的插件(记得在插件初始化中添加“transform:true”)。

ui.draggable需要通过转换的逆matrix传递,以便将元素放置在浏览器稍后转换的未转换空间中。

对于“可拖动”,以下工作对我来说(jqueryui 1.10)(matrix计算,我从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]; } } }, }); 

我有一个类似的问题与转换,并最终解决与CSS:

 transform-origin 

http://www.w3schools.com/cssref/css3_pr_transform-origin.asp

你试过了吗? 也许这将有所帮助。

我正在尝试通过gungfoo发布的可缩放大小变换scale()来修正显示在其实际大小的10%的元素,该方法没有工作。 在resize期间光标仍然远离元素。

我改变了resizeFix方法的最后两行,直接更新元素的宽度和高度,这解决了我的问题。

 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); } 

我发现我可以将缩放的元素换成另一个div,然后在其上设置可拖动的元素。

最好的答案是为我工作,直到我发现了一个可拖动小故障 🙁

当遏制本身也缩放:

  • 如果缩放<1,则不能在其收容箱内完全拖动
  • 如果缩放> 1,则可以将其拖出收容箱

幸运的是,我在这里find了一个解决scheme

这是我的解决scheme。 上面的一些代码不适合我。

我有一个可拖动的图像,我应用CSS转换旋转和缩放到该图像上。 一旦我缩放/旋转,位置在拖动开始时closures。 为了解决这个问题,我计算了从停止到开始的设定点差异,然后拖动应用一个新的设定值,

 var $img = $("#my-image-id"); $img.posLeft = 0; $img.posTop = 0; $img.diffLeft = 0; $img.diffTop = 0; $img.draggable({ start: function( event, ui ) { //check the difference from start to stop setpoints $img.diffLeft = $img.posLeft-ui.position.left; $img.diffTop = $img.posTop-ui.position.top; //console.log('start x: '+Math.floor(ui.position.left)+' y: '+Math.floor(ui.position.top)); //console.log('diff x: '+Math.floor($img.posLeft-ui.position.left)+' y: '+Math.floor($img.posTop-ui.position.top)); }, drag: function( event, ui ) { //fix the offset bug that is applied after CSS transform, to do that just add the value difference ui.position.left = ui.position.left+$img.diffLeft; ui.position.top = ui.position.top+$img.diffTop; //console.log('drag x: '+ui.position.left+' y: '+ui.position.top); }, stop: function( event, ui ) { //save the last applied setpoints $img.posLeft = ui.position.left; $img.posTop = ui.position.top; //console.log('stop x: '+Math.floor(ui.position.left)+' y: '+Math.floor(ui.position.top)); } }); 

CSS变换原点技巧确实只修正了缩放的bug。 旋转应该在中心附近,所以你想保持默认的50%50%。

我会加上这个作为对Gung Foo公认的答案的评论,但是我没有代表评论。

我发现Gung的回答对我来说是完美的,但是可调整的修复只在从右下angular拖动时才起作用。 我也有另外三个angular落的处理,发现元素会改变,所以我不得不从可拖动到可resize的function添加修复。

也许有更好的方法,或者我错过了一些东西,但是我发现下面修改的可resize适用于我所有的句柄:

 $(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; } }); 

我有同样的问题,最简单的方法是不使用jquery-ui的可拖动function,因为它不支持CSS3转换属性。

对我来说真正有效的是实现一个可拖动的function: http : //css-tricks.com/snippets/jquery/draggable-without-jquery-ui/

我知道这并不能解决你的问题100%,因为你仍然想使用jQuery的可拖动function,但它可以帮助其他人。

很长一段时间我想知道为什么@GungFoo解决scheme不为我工作的Jquery Resizable,但有人告诉它工作。 最后我发现使用jQuery Draggable和Resizable with CSS Transform Scale有4点:

  1. 使用@GungFoo解决scheme!
  2. 使用单独的div可拖动和可resize!
  3. 使用正确的CSS转换div的原点!
  4. 使用正确的CSS方向的div!

检查一下这个问题:

 function resizeDiv(event, ui) { debugger; var changeWidth = ui.size.width - ui.originalSize.width; var newWidth = ui.originalSize.width + changeWidth / 4; var changeHeight = ui.size.height - ui.originalSize.height; var newHeight = ui.originalSize.height + changeHeight / 4; ui.size.width = newWidth; ui.size.height = newHeight; }; $('#box').resizable({ minWidth: -($(this).width()) * 10, minHeight: -($(this).height()) * 10, resize: resizeDiv }); $('#box2').resizable({ minWidth: -($(this).width()) * 10, minHeight: -($(this).height()) * 10, resize: resizeDiv }); $('#box3').resizable({ minWidth: -($(this).width()) * 10, minHeight: -($(this).height()) * 10, resize: resizeDiv }); $('#box4').resizable({ minWidth: -($(this).width()) * 10, minHeight: -($(this).height()) * 10, resize: resizeDiv }); 
 .box{ position:absolute; width:30px; height:30px; border:1px solid black; font-size:3pt; overflow:hidden; -webkit-transform:scale(4); direction:ltr; } #box{ transform-origin: top left; top: 0; left:0; } #box2{ transform-origin: bottom left; top: 250px; left:0; } #box3{ direction:rtl; left: 250px; top: -10px; } 
 <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet" type="text/css" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script> <div id= 'box' class="box"> True styles </div> <div id= 'box2' class="box"> Bad transform-orgin </div> <div id= 'box3' class="box"> Bad direction </div> 

这些解决scheme都没有为我工作,至于其中一个,我不能改变可拖动的父母 – 只有可拖动的本身被转换。

这是我如何解决这个问题:

 $('.draggable').draggable( { cancel: '.restrict-drag', scroll: false, containment: '#contain', start: function(event, ui) { ui.position.left = 0; ui.position.top = 0; }, drag: function(event, ui) { ui.position.left = ui.position.left - ($(event.target).width() * Zoom); ui.position.top = ui.position.top - ($(event.target).height() * Zoom); } }); 

缩放是默认的0。

为了扩展可拖动,我做了以下操作:

 function changeZoom(zoom) { $('.draggable').each(function() { $(this).css('transform-origin', '50% 50%'); $(this).css('transform', 'scale(' + zoom + ')'); }); Zoom = zoom; // Global Zoom-variable } 

一切似乎都行得通。