Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug / feature req] Drag offset error when inside transform: scale() #139

Open
bjorsen opened this issue Feb 2, 2018 · 19 comments
Open

Comments

@bjorsen
Copy link

bjorsen commented Feb 2, 2018

We have a drag and drop environment that can be scaled by the end user (for instance zoom out to get a better overview).

What happens technically is that a CSS transform of scale is applied to the parent container of the drag and drop region. After this has happened, the drag and drop behaviour is "off". As soon as you click on a draggable item, it moves a distance away from its original position because the drag and drop behaviour does not take the transform into account.

@beefchimi
Copy link
Contributor

@bjorsen is there any chance you could cook up a example for me to use? You can either create a codepen, or open up a branch that adds a new example to the examples/ folder of this repo. It will help a lot 😄

Otherwise, perhaps you can provide some screenshots of your issue along with a few code snippets?

Thanks!

@tsov
Copy link
Member

tsov commented Feb 15, 2018

Let us know @bjorsen 🙂

@bjorsen
Copy link
Author

bjorsen commented Feb 15, 2018

Apologies all, it's been hectic and I haven't had the time to further look into this project. I'll try to make some time over the weekend to set up a minimalistic example with the isolated issue.

@jacobbridges
Copy link

👋 Hello @beefchimi and @tsov! I ran into this issue as well -- that is to say when dragging an item within a scaled container, not only is the initial drag offset off but also the item will get "out of sync" with the cursor the further it is moved from the origin point.

Example codepen: https://codepen.io/jacobbridges/pen/RQdwVQ
(Sorry for the random colors, just tried to make a simple example.)

@beefchimi
Copy link
Contributor

beefchimi commented Mar 1, 2018

Hey @jacobbridges , thanks for making a Codepen!

I tinkered with it a bit... but something else was funky and the solution I had in mind wasn't behaving. So, I'm going to experiment with this in the Examples project when I get the chance... hopefully over the weekend.

Apologies for the delay on resolving this. I'll investigate and report back 👍

@bjorsen
Copy link
Author

bjorsen commented Mar 2, 2018 via email

@beefchimi
Copy link
Contributor

@bjorsen very sorry to hear about that – hope everything is alright.

This is indeed a bug... and a very strange one.

Even if my container has just transform: scale(1)... the mirror will still be heavily offset! The presence of scale alone causes the problem. And its not simply a stacking context conundrum... adding/removing position: relative makes no difference.

I have yet to figure out the solution but I'm going to continue looking into this. I want to see first what CSS sorcery is causing this issue... and depending on what I find, I may have to bug @tsov to consider a JS solution.

Will update this thread once I've had the chance to dig deeper.

Hang tight my friends!

@bzle
Copy link

bzle commented Mar 5, 2018

@beefchimi I'm also having an offset problem when the parent/container has transform: translate.

@beefchimi
Copy link
Contributor

Sorry for the late reply. To give some insight into the CSS behaviour:

a transformed element creates a containing block for all its positioned descendants
http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/

Basically, once you have a transform on a container, any children of that container will be positioned relative to that parent. So, instead of position: fixed; being relative to the viewport, it is now relative to the transformed parent.

Depending on how you have built your styling / how your app needs to function, I believe in most cases this can be easily resolved by using the appendTo option to append the mirror higher up in the DOM.

Example:

export default function Example() {
  const sortable = new Sortable(document.querySelector('.TransformedList'), {
    appendTo: 'body',
  });

  return sortable;
}

Here, we simply append our mirror element to the body, rather than its direct parent (presumably, .TransformedList). Problem solved.

Now, for the instances where a consumer must have the mirror appended within that Draggable container... I'm entertaining the idea of adding a new option called relativeToElement, which would take a single selector or Node, and do some offset calculations based on that "elements" BoundingClientRect.

I don't love this at the moment... as it muddies up a few things, such as the cursorOffset options. @tsov and I would need to discuss the best solution to this.

I'll leave this issue open so @tsov and I can discuss the best options.

@MateuszSapielakGSI
Copy link

MateuszSapielakGSI commented Nov 12, 2018

@beefchimi did relativeToElement ever make it to the library? I resolved my problem appending to the body but this would be a much cleaner solution in my case.

scratch that, turns out I could just get rid of the transform in my case

Thanks!

@marian-r
Copy link

@beefchimi any movement on this? I would need this feature for a HTML game which uses scaling for the main scene.

@joriskoris
Copy link

Any news on this now?

@silencio
Copy link

silencio commented Feb 8, 2019

The appendTo option did solve the problem in my case.

However there's a slight typo in @beefchimi's example: appendTo being a mirror option, not a "root" option.

It should be this:

export default function Example() {
  const sortable = new Sortable(document.querySelector('.TransformedList'), {
    mirror: {
      appendTo: 'body',
    }
  });
  return sortable;
}

instead of this:

export default function Example() {
  const sortable = new Sortable(document.querySelector('.TransformedList'), {
    appendTo: 'body',
  });

  return sortable;
}

@izhan
Copy link

izhan commented Feb 27, 2019

@beefchimi Any update on this? I'm running into a similar issue with building a Chrome extension. I unfortunately can't restructure the DOM or CSS so appendTo will not work for me.

As a hacky workaround, I've forked the repo, made the container a containing block on drag start by adding transform: translate(0), and modified Mirror.js to position it relative to the container instead of window. But curious what your take on a workaround would be.

@Vitj6
Copy link

Vitj6 commented Oct 30, 2019 via email

@hbalardin
Copy link

Hi, everyone!

Any progress on this? I really need that mirror scales with it's parent element. I can't use "appendTo" to fix offsets because in that way, the scale styles does not applies to the mirror...

@kostyay
Copy link

kostyay commented Oct 26, 2021

Any progress? The appendTo fix doesnt seem to work
https://codepen.io/kostyay/pen/vYJxoze

@djimnz
Copy link

djimnz commented Mar 10, 2023

Hello,

My workaround was to use mirror.appendTo as well as listening mirror:created to override the transform scale and reset the transform-origin, as below:

new Sortable(containers, {
        draggable: `.${classNames.draggableItem}`,
        mirror: {
          // ...
          appendTo: 'body'
        }
      }).on('mirror:created', event => {
          event.data.mirror.children[0].style.transform = `scale(${zoomScale /
            100})`;
          event.data.mirror.children[0].style.transformOrigin = '0 0';
        });

I recommend you wrap your draggable item children into a single element.
Please, expect to have some hacky CSS things to do.

Not the best way to handle it, but it works, and this might give you some ideas for your own research.

@pdemidyuk-ib
Copy link

.on('mirror:created', event => {
          event.data.mirror.children[0].style.transform = `scale(${zoomScale /
            100})`;
          event.data.mirror.children[0].style.transformOrigin = '0 0';
        });

doesn't scale for me for some reason. the mirror is still unscaled

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests