[bug / feature req] Drag offset error when inside transform: scale()
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.
@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!
Let us know @bjorsen π
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.
π 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.)
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 π
Hi @jacobbridges, thanks for picking up my slack in putting up an example illustrating the issue. I've been dealing with medical issues in the family every spare moment lately.
On Thu, 1 Mar 2018, 23:13 Curtis Dulmage, [email protected] wrote:
Hey @jacobbridges https://github.com/jacobbridges , thanks for making a Codepen!
I tinkered with it a bit... but something else was funky and the solution I had in mind was 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 π
β You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Shopify/draggable/issues/139#issuecomment-369749468, or mute the thread https://github.com/notifications/unsubscribe-auth/ABLFLtGqu3T-Ko5VWDIvXScvOalKWS3oks5taHKigaJpZM4R2-78 .
@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!
@beefchimi I'm also having an offset problem when the parent/container has transform: translate.
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.
~~@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!
@beefchimi any movement on this? I would need this feature for a HTML game which uses scaling for the main scene.
Any news on this now?
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; }
@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.
ΠΏΡ, 23 Π½ΠΎΡΠ±. 2018 Π³., 0:15 Marian Rusnak [email protected]:
@beefchimi https://github.com/beefchimi any movement on this? I would need this feature for a HTML game which uses scaling for the main scene.
β You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Shopify/draggable/issues/139#issuecomment-441106532, or mute the thread https://github.com/notifications/unsubscribe-auth/Ae_lgVGn3uT9HZOF93AneeOgSHQjdw3tks5uxve7gaJpZM4R2-78 .
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...
Any progress? The appendTo fix doesnt seem to work
https://codepen.io/kostyay/pen/vYJxoze
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.
.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