jQuery UI Draggable + CSS Transform Causes “Jumping”

后端 未结 6 1185
既然无缘
既然无缘 2021-02-08 06:09

EDIT: I solved it. But StackOverflow isn\'t letting me mark my answer as the solution, so I simply am not going to.

I\'m having an issue with regard to

相关标签:
6条回答
  • 2021-02-08 06:24

    I found the solution.

    The solution is the completely avoid position:absolute; when using Draggable and CSS transforms. You can easily get manipulate anything from absolute/window/whatever coordinates into relative, so that's just what I did.

    In my case, I was spawning a Draggable element underneath the mouse. I calculated the relative position based on the mouse position with the offset() of the element (both in window coordinates) and then divided by the scale of the parent div.

    Here's a snippet:

    // ops.[x|y] is the mouse position in window coords
    // parentDiv.offset().[left|right] is the div position in window coords
    
    // get the scale transform matrix from our poorly written panzooming lib
    var mtx = graph.parentDiv.panzoom('getMatrix');
    var zx = mtx[0];
    var zy = mtx[3];
    
    // calculate the relative position
    var x = (ops.x - parentDiv.offset().left) / zx;
    var y = (ops.y - parentDiv.offset().top) / zy;
    
    // set some initial css
    parentDiv.css('position', 'relative')
             .css('left', x + 'px')
             .css('top', y + 'px');
    
    // initialize the draggable
    parentDiv.draggable({
        stack: $(graph.parentDiv).children(),
        drag: function(e, ui){
            var mtx = graph.parentDiv.panzoom('getMatrix');
            var zoomScaleX = mtx[0];
            var zoomScaleY = mtx[3];
    
            // scale the delta by the zoom factor
            var dx = ui.position.left - ui.originalPosition.left;
            var dy = ui.position.top - ui.originalPosition.top;
    
            ui.position.left = ui.originalPosition.left + (dx / zoomScaleX);
            ui.position.top = ui.originalPosition.top + (dy / zoomScaleY);
        }
    });
    
    0 讨论(0)
  • 2021-02-08 06:28

    Expanding on "raghugolconda" top answer:

    I've had dragging speed and jumping problems with jQueryUI draggable and CSS transform: scale()

    The image container is scalable with zoom slider, the red square is draggable.

    What happened when i tried to drag the red element:

    • Element jumped when you tried to drag it
    • Drag speed was too low when Zoom was over 100%
    • Drag speed was too fast when Zoom was under 100%

    Fix:

    1. Calculate fraction (scale value) from jQuery slider. Here is my slider than transforms image container:

      var fraction = 1;  
      
      $("#slider").slider({
          value: 0,
          min: -70,
          max: 70,
          step: 10,
          slide: function (event, ui) {
              fraction = (1 + ui.value / 100);  
      
              $("#infoSlider").text('Zoom: ' + Math.floor(fraction * 100) + '%');
      
              $('.image_scalable_container').css({
                  '-webkit-transform': 'scale(' + fraction + ')',
                  '-moz-transform': 'scale(' + fraction + ')',
                  '-ms-transform': 'scale(' + fraction + ')',
                  '-o-transform': 'scale(' + fraction + ')',
                  'transform': 'scale(' + fraction + ')'
              });
      
          }
      });
      
    2. Overwrite jQuery UI draggable drag and start functions.

    In drag you modify the dragging speed (scale 0.9 means drag_speed = 1 / 0.9 = 1.11 )

    Here is my example:

    $("#marker").draggable({
        //revert: true,
        zIndex: 100,
        drag: function (event, ui) {
            var drag_speed = 1 / fraction;
    
            __dx = (ui.position.left - ui.originalPosition.left) * drag_speed;
            __dy = (ui.position.top - ui.originalPosition.top) * drag_speed;
            ui.position.left = ui.originalPosition.left + (__dx);
            ui.position.top = ui.originalPosition.top + (__dy);            
            ui.position.left += __recoupLeft;
            ui.position.top += __recoupTop;
        },        
        start: function (event, ui) {            
            //resize bug fix ui drag
            var left = parseInt($(this).css('left'), 10);
            left = isNaN(left) ? 0 : left;
            var top = parseInt($(this).css('top'), 10);
            top = isNaN(top) ? 0 : top;
            __recoupLeft = left - ui.position.left;
            __recoupTop = top - ui.position.top;
        },
    });
    
    0 讨论(0)
  • 2021-02-08 06:31

    position:absolute; is indeed problematic. I however found an alternative solution that prevents the jump, while keeping an absolute basis for coordinates and maintain position, by flipping the css position to relative on mousedown, and restore it to absolute on mouseup, such as the following:

    $('#container').on('mousedown', 'canvas', function (e) {
        e.currentTarget.style.position = 'relative';
    }).on('mouseup', 'canvas', function (e) {
        if (e.currentTarget.style.position !== 'absolute'){
            e.currentTarget.style.position = 'absolute';
        }
    });
    

    It works well for mouse events. And to resolve the issue for touch events, along with the 'touchpunch' plugin, I additionally also had to cancel 'click' events (for mobile and touch enabled modality only).

    0 讨论(0)
  • 2021-02-08 06:35

    A much simpler solution is to wrap the scaled content with another div and set that to be draggable instead.

    0 讨论(0)
  • 2021-02-08 06:40
    //css3 transform bug with jquery ui drag - fixed(works fine whether position, absolute or relative)
    var __dx;
    var __dy;
    var __scale=0.5;
    var __recoupLeft, __recoupTop;
    
    $("#draggable").draggable({
        //revert: true,
        zIndex: 100,
        drag: function (event, ui) {
            //resize bug fix ui drag `enter code here`
            __dx = ui.position.left - ui.originalPosition.left;
            __dy = ui.position.top - ui.originalPosition.top;
            //ui.position.left = ui.originalPosition.left + ( __dx/__scale);
            //ui.position.top = ui.originalPosition.top + ( __dy/__scale );
            ui.position.left = ui.originalPosition.left + (__dx);
            ui.position.top = ui.originalPosition.top + (__dy);
            //
            ui.position.left += __recoupLeft;
            ui.position.top += __recoupTop;
        },
        start: function (event, ui) {
            $(this).css('cursor', 'pointer');
            //resize bug fix ui drag
            var left = parseInt($(this).css('left'), 10);
            left = isNaN(left) ? 0 : left;
            var top = parseInt($(this).css('top'), 10);
            top = isNaN(top) ? 0 : top;
            __recoupLeft = left - ui.position.left;
            __recoupTop = top - ui.position.top;
        },
        stop: function (event, ui) {
            $(this).css('cursor', 'default');
            //alternate to revert (don't use revert)
            $(this).animate({
                left: $(this).attr('oriLeft'),
                top: $(this).attr('oriTop')
            }, 1000)
        },
        create: function (event, ui) {
            $(this).attr('oriLeft', $(this).css('left'));
            $(this).attr('oriTop', $(this).css('top'));
        }
    });
    
    0 讨论(0)
  • 2021-02-08 06:45

    Here is a working example showing raghugolconda's method.

    I utilized the data method, instead of setting non-standard attr values.

    I wrapped it all into a jQuery plugin called $.fn.draggablePatched.

    function main() {
      $('#draggable').draggablePatched({
        cursor: 'pointer'
      });
    }
    
    /* jquery.draggable-patched.js */
    (function($) {
      var __dx, __dy;
      var __recoupLeft, __recoupTop;
    
      var parseIntSafe = function(value) {
        return (function(n) {
          return isNaN(n) ? 0 : n;
        })(parseInt(value, 10));
      }
    
      $.fn.draggablePatched = function(options) {
        options = options || {};
        return this.draggable({
          cursor: options.cursor || 'move',
          zIndex: 100,
          drag: function(event, ui) {
            __dx = ui.position.left - ui.originalPosition.left;
            __dy = ui.position.top - ui.originalPosition.top;
            ui.position.left = ui.originalPosition.left + __dx + __recoupLeft;
            ui.position.top = ui.originalPosition.top + __dy + __recoupTop;
            if (options.drag) {
              options.drag(event, ui);
            }
          },
          start: function(event, ui) {
            var left = parseIntSafe($(this).css('left'));
            var top = parseIntSafe($(this).css('top'));
            __recoupLeft = left - ui.position.left;
            __recoupTop = top - ui.position.top;
            if (options.start) {
              options.start(event, ui);
            }
          },
          stop: function(event, ui) {
            $(this).animate({
              left: $(this).data('oriLeft'),
              top: $(this).data('oriTop')
            }, 1000);
            if (options.stop) {
              options.stop(event, ui);
            }
          },
          create: function(event, ui) {
            $(this).data({
              oriLeft: $(this).css('left'),
              oriTop: $(this).css('top')
            });
            if (options.create) {
              options.create(event, ui);
            }
          }
        });
      }
    })(jQuery);
    
    main();
    body {
      background-color: blue;
    }
    
    #draggable {
      position: absolute;
      left: 50px;
      top: 50px;
      width: 350px;
      height: 350px;
      
      background-color: rgba(0, 0, 0, 0.5);
      border: 1px solid black;
      color: white;
      
      font-size: 4em;
      line-height: 350px;
      text-align: center;
      
      -moz-transform: rotate(-45deg) scale(0.5);
      -webkit-transform: rotate(-45deg) scale(0.5);
      transform: rotate(-45deg) scale(0.5);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <div id="draggable">
      Hello!
    </div>

    0 讨论(0)
提交回复
热议问题