Jump to content
GreenSock

Skid X

[Draggable] Bug while dragging inside rotated element (with solution)

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hi,

I would like to report a bug found using Draggable (great library!), hope this is the right place.

If you drag an element (x,y) inside a parent that has been previously rotated, then this.x and this.y values inside the onDrag callback refer to horizontal and vertical directions, ignoring the fact that the parent is rotated.
The result is that if, for example, you rotate the parent by 90deg, when you drag the child horizontally it moves vertically and viceversa.

The fix however seems easy. Let's say we have the angle of the parent rotation expressed in degree in the variable 'angle'. Then inside the callback onDrag:
 

angle = - angle * Math.PI / 180;
var cosAngle = Math.cos(angle);
var sinAngle = Math.sin(angle);
var relativeX = (this.x * cosAngle) - (this.y * sinAngle);
var relativeY = (this.x * sinAngle) + (this.y * cosAngle);

relativeX and relativeY are now the corrected values relative to the parent of the Draggable element.
Hope this could be useful.

Thanks for your great libraries.
 

  • Like 1
Link to comment
Share on other sites

Yeah, Draggable wasn't intended to be used inside rotated/scaled/skewed ancestors because that actually gets much more tricky and can sap performance. Your solution only works in one very particular scenario, but what if the parent's parent was also rotated and/or skewed and/or scaled? What if it uses a transformOrigin that's not dead-center? See how complex it gets? To accommodate all those scenarios, Draggable would need to walk up the DOM tree, record each ancestor's matrix, combine them, and then invert that and use it to project the mouse coordinates on every single update. Not only would that hurt performance but it'd eat up more kb in Draggable and exact a penalty for everyone even though I doubt that even 1% of the users would ever need to accommodate a rotated/transformed parent, so it's tough for me to justify it. See what I mean? 

Link to comment
Share on other sites

Yes, of course.

I've proposed that solution for any users that could have my same scenario or a similar one, not as a bulletproof fix for the overall library.

As you told, clearly going up the tree every time and making all those checks and operations also when not needed is not suitable as a default behaviour.

Just a simple suggestion for users in the same trouble as mine :)

Link to comment
Share on other sites

Yes, and thank you for posting the solution you used. Very helpful.

Link to comment
Share on other sites

  • 1 month later...

I know this is an older thread, but I wanted to let you know that as of today, Draggable has been updated so that it can work inside transformed (rotated/scaled) ancestor elements! It was quite an undertaking to figure out how to do it in a performative way, but I think you'll find that it works quite well. Please take a peek and let us know if it delivers the behavior you were after. 

 

http://www.greensock.com/?download=GSAP-JS

  • Like 3
Link to comment
Share on other sites

Great, thanks!
I will test it in next week, and will provide feedback if needed.

Just a question: does it offer only x and y properties (now transformed) or are there a couple of properties (like "before" and "after" transformation)?

 

Link to comment
Share on other sites

I'm not sure I understand what you mean. Can you elaborate? 

 

By the way, I'm actually working on a tool that'd allow you to translate any point (x/y) from one coordinate space to another in the DOM, factoring in all the translations. So imagine you've got a "child" element inside a "parent" element that's rotated inside a "grandparent" that's scaled inside a "great-grandparent" that's skewed, and you want to figure out exactly where the origin of that child lands in the global scope of the page. Or maybe you've got a point that's 100px to the right, and 30px down from the child's origin (inside the child's local coordinate space) and you want to plot out where that is (with all transformations) in the global page context (how far down from the top and left of the page itself). My goal is to make that really easy. I haven't seen any other tools out there that do it, but please let me know if you're aware of any. It's not quite ready yet, and it will likely be a Club GreenSock membership benefit (it's a pretty advanced concept), but if you'd like to help test it when it's ready, please contact me. 

 

In any case, I don't understand what you meant by "before" and "after" transformation in the context of Draggable, so please elaborate. 

  • Like 1
Link to comment
Share on other sites

Sorry, my english sucks, I try to explain better.

Before your last update, during drag events this.x and this.y values referred always horizontal and vertical offsets also if the parent was rotated.

So I was wondering if now, after your update, this.x and this.y refer to already transformed values (so for example if parent is rotated by 90deg and then I drag the child +50px horizontally relative to the screen, then this.x will not change while this.y will be -50px) or if the library now offers two couple of properties (using my previous example: this.x = +50px, this.y = 0, this.relativeX = 0, this.relativeY = -50px).

Or maybe there is a third case: you have left the values of this.x and this.y unmodified and you have fixed only the real position of the DOM element without adding any other public properties.

I don't know any available tool that works like you said, it would be definitely a "must-have"!
 

Link to comment
Share on other sites

this.x and this.y would always reflect the element's "transform" css values, and those are always relative to the element's local coordinate system (well, I guess you could consider it to be its parent's coordinate space). So if I move it 100px to the right (x), and then I rotate its PARENT element 90 degrees, that doesn't change the element's "x" at all (because you rotated the parent, not the element itself). 

 

That's one of the beauties of the tool I'm building, though - it allows you to translate between contexts. So you could find out exactly where that x:100, y:0 point was located in the global context (or ANY context - according to any element's coordinate system).

 

Does that answer your question?

Link to comment
Share on other sites

I look forward to that new tool your building Jack.. it will be very interesting to be able to translate between contexts :)

Link to comment
Share on other sites

Does that answer your question?

 

Yes, thanks.

Link to comment
Share on other sites

I've set up this simple codepen to show how the updated Draggable tool works in a transformed parent element, that also acts as the child bounds.

 

The transforms include 2D rotation, 3D rotations, Skew and scale. Also you can mix them in order to see how good this feature works.

 

See the Pen KAFow by rhernando (@rhernando) on CodePen

 

Although I recommend you to use the full screen view:

See the Pen KAFow by rhernando (@rhernando) on CodePen

  • Like 1
Link to comment
Share on other sites

Thanks Rodrigo. I forgot to mention that technically it doesn't fully support 3D transforms (only 2D because full 3D support would require 4 times as many math operations and it's pretty unlikely anybody is actually going to want to use Draggable in 3D space for real-world projects), but it looks like it does relatively well with 3D in your demos, so that's good :)

Link to comment
Share on other sites

That's funny, the numbers come up as they should, even if you combine 2D with 3D, perhaps the child having a relative position and the fact that the draggable instance works on the x and y values of the element.

 

I'll experiment a little more and post the results.

 

Rodrigo.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×