使用 CSS 变换缩放的 jQuery 拖动/调整大小

Posted

技术标签:

【中文标题】使用 CSS 变换缩放的 jQuery 拖动/调整大小【英文标题】:jQuery Drag/Resize with CSS Transform Scale 【发布时间】:2012-04-30 01:24:51 【问题描述】:

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

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

到一个 div,然后在该 div 的子级上使用 jQuery 的 draggable() 和 resizable() 插件。

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

我在 *** 上找到了一个解决方案(尽管我愚蠢地没有为它添加书签,现在找不到它....),它建议修补插件,并且效果很好。它沿着这条线走:

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

  

非常感谢提出这个建议的人。

所以,我的问题!有没有人遇到过一种在不需要修补 jQuery 的缩放元素中具有可拖动/可调整大小事件的好方法?我用谷歌搜索过,这是我能找到的最好的解决方案。有谁知道可能在这些条件下使用 CSS 转换的 jquery 替代方案?

非常感谢您的任何回复。

【问题讨论】:

我不确定它是否 100% 相关,但自从你发布了这个问题,我回答了一些不完全不同的 here。当时的解决方案是提供一个drag 回调来强制自定义容器来代替可拖动的本机容器行为,该行为被容器的动态调整大小所破坏。 我明白你的意思,这是一个类似的情况,父/包含元素正在以一种方式被操纵 jquery 不检查(正如你所不希望的那样)。回调是个好主意,下次我会记住这一点。最后,我编写了一个专门针对我正在做的事情的 jquery ui 小部件工厂扩展。如果我有时间,我会清理掉我的客户代码并将其放在这里。 将错误报告给bugs.jquery.com/newticket?redirectedfrom=http://… Imdad,这不是一个错误,它缺少绝大多数开发人员不需要的功能,实施它对许多人来说会浪费性能。无论如何,我没有完整的解决方案,但我有一个事件脚本,欢迎您调整(可能对您来说更容易):extraordinarythoughts.com/2012/02/13/… @Shawn 该脚本看起来很有用,可能会像您所说的那样适应。谢谢 【参考方案1】:

这个问题被问到已经有一段时间了。我找到了(实际上创建了)一个答案。它所需要的只是设置回调处理程序。无需编辑 jquery-ui!

注意:此示例中的 zoomScale 是一个全局变量,并且使用 animate 设置转换(由 jquery.transform.js 辅助),如下所示:

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

看看这个:

转换 scale() 以调整大小:

$(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 resizable/draggable with transform: scale() set

【讨论】:

使用开始手柄会破坏我的拖动手柄。如果我不使用它,会有一个初始偏移量,但至少可以拖动。 请提供示例并提供您使用的浏览器!可能是代码错误,因为我使用的是.drag-handle,请查看句柄参数,而您的代码中可能没有这个参数。尝试只使用$(this) 而不是$('.drag-handle',this) 我看到你修好了拖动手柄,但我马上就修好了。问题在于,最新的 jqueryui 覆盖 start 中的 ui.position 会破坏 drag 中的等式比例,并且坐标会得到负值。我目前没有独立的测试用例,但我想我还是得准备一个 一切对我来说都很好,因为我也在制作一个正在转变的容器。我唯一的小毛病是,当我开始调整大小时,我有点向上跳跃。有什么线索吗?我正在使用 $(this) 而不是 contentElement。是这个问题吗? 在“调整大小”中,您需要对 ui.position.leftui.position.top 执行相同操作【参考方案2】:

我正在尝试 gungfoo 在显示为实际大小的 10% 的元素上发布的 transform scale() 修复可调整大小,但该方法不起作用。在调整大小期间,光标仍然远离元素。

我更改了 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);

【讨论】:

这个答案在宽高比锁定方面非常有用!我不确定 JQuery UI 在使用包裹的 ui.size / ui.position 的幕后做了什么,但在应用缩放的同时使锁定的纵横比工作的唯一方法是使用这种方法。除了顶部和左侧计算,使用 ui.originalElement.css('top', newTop) 和 ui.originalElement.css('left', newLeft) 设置。谢谢!【参考方案3】:

我在转换时遇到了类似的问题,最终用 css 解决了它:

transform-origin

http://www.w3schools.com/cs-s-ref/css3_pr_transform-origin.asp

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

【讨论】:

我将其设置为 0,0,更改它只会影响变换的原点。谢谢【参考方案4】:

我自己的“答案”是在这种情况下调整 jQuery UI 可拖动以进行单独的交互,称为“可拖动”。

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

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

仍然想听听任何替代方案...

【讨论】:

我在拖动转换后的元素时遇到了类似的问题,Paul 的 traggable 脚本运行良好。比monkeyPatch_mouseStart hack 好得多。谢谢保罗。 我创建了(因为我不得不)基于可调整大小和可拖动的回调的解决方案,看看! @GungFoo 我会接受你的回答,它最符合这个问题,因为它不需要重写任何 jQuery 或开发你自己的插件。不错的一个 不必重写 jqueryui 是我想出这个的原因。如果它帮助了某人,我很高兴:)【参考方案5】:

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

ui.draggable 需要通过变换的逆矩阵传递,以便将元素定位在未变换的空间中,浏览器稍后在显示时变换。

对于“可拖动”,以下对我有用(jqueryui 1.10)(我从 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];
            
        
    ,     
);

【讨论】:

【参考方案6】:

在我发现可拖动的故障之前,最佳答案对我有用:(

当容器本身也被缩放时:

如果缩放,则无法在其包含框中完全拖动 如果缩放 > 1 则可以将其拖出收容箱

幸运的是,我找到了解决方案here

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

很长一段时间以来,我想知道为什么 @GungFoo 解决方案不适用于我的 Jquery Resizable 但有些人告诉它有效。最后我发现使用 jQuery Draggable and Resizable with CSS Transform Scale 有 4 点:

    使用@GungFoo 解决方案! 使用单独的 div 实现可拖动和调整大小! 为 div 使用正确的 CSS 变换原点! 对 div 使用正确的 CSS 方向!

检查其中的一些问题:

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>

感谢@Guy ;)

【讨论】:

【参考方案9】:

这是我的解决方案。上面发布的一些代码对我不起作用。

我有一个可拖动的图像,我在该图像上应用 CSS 变换旋转和缩放。一旦我缩放/旋转它,位置就会在拖动开始时关闭。为了解决这个问题,我计算了从停止到开始的设定点差异,并在拖动时应用一个新的设定点,并将差异考虑在内。

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 transform-origin 技巧只修复了缩放错误。旋转应该围绕中心,所以你要保持默认的 50% 50%。

【讨论】:

【参考方案10】:

我会将此作为评论添加到 Gung Foo 接受的答案中,但我没有代表发表评论。

我发现 Gung 的回答对我来说非常有效,但是可调整大小的修复仅在从右下角拖动时才有效。我在其他三个角上也有手柄,发现元素会移动,所以我必须将修复从可拖动添加到可调整大小的函数。

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

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

    
);

【讨论】:

【参考方案11】:

我遇到了同样的问题,对我来说最简单的方法是不要使用 jquery-ui 的可拖动功能,因为它不支持 CSS3 转换属性。

对我来说真正有用的是实现自己的可拖动功能:http://css-tricks.com/snippets/jquery/draggable-without-jquery-ui/

我知道它不能 100% 解决您的问题,因为您仍然想使用 jquery-ui 可拖动功能,但它可以帮助其他人。

【讨论】:

已经有一段时间了,但这实际上是我们最终所做的。【参考方案12】:

这些解决方案都不适合我,至于其中一个,我无法转换可拖动对象的父对象 - 只有可拖动对象本身被转换。

这就是我解决此问题的方法:

$('.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

一切似乎都正常。

【讨论】:

【参考方案13】:

对于不同的调整角

$('.rect-item').resizable(
        handles: 'n,e,s,w,ne,se,sw,nw',
        resize: (e, ui) => 
          const zoomScale = Your scale;
          const changeWidth = ui.size.width - ui.originalSize.width; // find change in width
          const newWidth = ui.originalSize.width + changeWidth / zoomScale; // adjust new width by our zoomScale

          const changeHeight = ui.size.height - ui.originalSize.height; // find change in height
          const newHeight = ui.originalSize.height + changeHeight / zoomScale; // adjust new height by our zoomScale

          ui.size.width = newWidth;
          ui.size.height = newHeight;

          // here
          const posOrigin = ui.originalPosition;
          if (posOrigin.left !== ui.position.left) 
            ui.position.left = posOrigin.left - changeWidth / zoomScale;
          
          if (posOrigin.top !== ui.position.top) 
            ui.position.top = posOrigin.top - changeHeight / zoomScale;
          

        
      );

【讨论】:

以上是关于使用 CSS 变换缩放的 jQuery 拖动/调整大小的主要内容,如果未能解决你的问题,请参考以下文章

Jquery可拖动缩放问题

使用jQuery可拖动在div剪贴蒙版中拖动缩放图像?

使用 CSS 变换在悬停时缩放 SVG 矩形

CSS3 使用 JavaScript 拖动转换 3d 变换

使用 CSS3 变换比例缩放一个点

每天学一个jquery插件-缩放与拖动