11

I'm scaling a JQuery sortable element with CSS transform. Both the sortable items start positions and offsets while dragging are wrong beacuse JQuery doesn't take CSS scales into consideration. I solved it partially with code found here:

jQuery Drag/Resize with CSS Transform Scale

But the thing I cannot solve is the sortable item position at drag starts. It jumps up and right a bit. I can't figure out what to put into the start event handler:

        start: function(e, ui)
        {
            // something is needed here to fix the initial offset
        }

This Fiddle shows the problem: http://jsfiddle.net/adrianrosca/zbvLp/4/

Community
  • 1
  • 1
Adrian Rosca
  • 6,922
  • 9
  • 40
  • 57
  • I started a bounty for this question. Although I don't necessarily think this question is widely applicable to a large audience, I *do* like to see a canonical answer that addresses what exactly needs to be adjusted in the sortable event handlers and why the jquery draggable solution, that was suggested in OP's [referenced SO question](https://stackoverflow.com/q/10212683/165154), is not applicable to jquery sortables. – Decent Dabbler Apr 17 '16 at 20:17

2 Answers2

9

One difference with draggable is that the transform is not on the elements themselves, but on the parent. So it changes a bit the logic.

Here's a solution for this specific case, but you'll see that depending on the situation it may change. For example, if you change transform-origin, or if you have an horizontal sortable, it'll have to be adapted. But the logic stays the same:

var zoomScale = 0.5;

$(".container")
  .sortable({

    sort: function(e, ui) {
    console.log(ui.position.left)
      var changeLeft = ui.position.left - ui.originalPosition.left;
      // For left position, the problem here is not only the scaling,
      // but the transform origin. Since the position is dynamic
      // the left coordinate you get from ui.position is not the one
      // used by transform origin. You need to adjust so that
      // it stays "0", this way the transform will replace it properly
      var newLeft = ui.originalPosition.left + changeLeft / zoomScale - ui.item.parent().offset().left;

      // For top, it's simpler. Since origin is top, 
      // no need to adjust the offset. Simply undo the correction
      // on the position that transform is doing so that
      // it stays with the mouse position
      var newTop = ui.position.top / zoomScale;

      ui.helper.css({
        left: newLeft,
        top: newTop
      });
    }
  });

http://jsfiddle.net/aL4ntzsh/5/

EDIT:

Previous answer will work for positioning, but as pointed by Decent Dabbler, there is a flaw with the intersection function that validates when a sorting should occur. Basically, positions are correctly calculated, but the items keep width and height values that are not transformed, which is causing the problem. You can adjust these values by modifying them on start event to take scale factor into account. Like this for example:

 start: function(e, ui) {
      var items = $(this).data()['ui-sortable'].items;
      items.forEach(function(item) {
        item.height *= zoomScale;
        item.width *= zoomScale;
      });
    }

http://jsfiddle.net/rgxnup4v/2/

Julien Grégoire
  • 16,864
  • 4
  • 32
  • 57
  • Both your and [user3765122's answer](https://stackoverflow.com/a/36715198/165154) seem to solve OP's problem, to an equally large extent. However, a subtle problem, which I suspect is the culprit to my own problem, appears to remain in both: the sort detection is shifted to the right. If you grab an element in the horizontal center and drag it over others, while carefully shifting left and right, you'll see this. Since I use connected sortables, which are much wider *and* horizontal siblings as well, this subtle problem is extremely magnified. Do you know how to compensate for this? – Decent Dabbler Apr 21 '16 at 11:20
  • Maybe I'll just have to start a new question for my specific case, because it's much more complex than OP's, but my problem might disappear if you know how to fix this subtle issue. In any case: since both your answers appear to be on an equal footing thus far, I'm not entirely sure which answer to award the bounty yet. I might decide to award a bounty to both. But perhaps you could explain why both answers appear to have the same outcome? PS.: Thank you very much for your solutions so far! – Decent Dabbler Apr 21 '16 at 11:20
  • Okay, thank you very much for the addition! This *does* solve the issue for OP's case. Unfortunately it doesn't solve my case with my connected sortables (the connected items all show as having `{width: 0, height: 0, left: 0, top: 0}`), but like I said, my case is much more complex. So, either I'm going to fiddle with it some longer myself to really try to understand the intricacies of positionings/offsets/etc., or I'll start a new question about it if I'm unable to figure it out. But the bounty is well deserved, and you've given me lots of clues about where to look. Thanks again! – Decent Dabbler Apr 23 '16 at 14:55
1

There are two things in your problem:

  1. Position property of a element is calculated based on nearest parent which has position is absolute or relative. Right now based parent is body which is not good so you have to add position:relative; for container.
  2. Because, as you already notice, JQuery doesn't take CSS scales into consideration when calculate both ui.position and ui.originalPosition so you have to "apply" zoomScale to both of them.

CSS

.container
{
    position: relative;
    transform: scale(0.5);
    transform-origin: center top;
}

Script

var changeLeft = ui.position.left - ui.originalPosition.left;
var newLeft = (ui.originalPosition.left + changeLeft) / zoomScale;
var changeTop = ui.position.top - ui.originalPosition.top;
var newTop = (ui.originalPosition.top + changeTop) / zoomScale;

This is update code: http://jsfiddle.net/zbvLp/9/

Windian
  • 56
  • 3
  • Both your and [Julien Grégoire's answer](https://stackoverflow.com/a/36684822/165154) seem to solve OP's problem, to an equally large extent. However, a subtle problem, which I suspect is the culprit to my own problem, appears to remain in both: the sort detection is shifted to the right. If you grab an element in the horizontal center and drag it over others, while carefully shifting left and right, you'll see this. Since I use connected sortables, which are much wider *and* horizontal siblings as well, this subtle problem is extremely magnified. Do you know how to compensate for this? – Decent Dabbler Apr 21 '16 at 11:19
  • Maybe I'll just have to start a new question for my specific case, because it's much more complex than OP's, but my problem might disappear if you know how to fix this subtle issue. In any case: since both your answers appear to be on an equal footing thus far, I'm not entirely sure which answer to award the bounty yet. I might decide to award a bounty to both. But perhaps you could explain why both answers appear to have the same outcome? PS.: Thank you very much for your solutions so far! – Decent Dabbler Apr 21 '16 at 11:20