Jump to content
Search Community

Strange Orbit around a point in addition to Rotation when animating an SVG rotation

Calsa test
Moderator Tag

Go to solution Solved by ZachSaucier,

Recommended Posts

I am using NPM and Typescript.

 

I have a strange issue, and I have stumped myself trying to figure out how to fix it.  Somehow using GS to rotate an SVG element is not causing rotation to occur as anticipated when I am moving the cx and cy points of the same SVG circle element.

 

This issue only is noticeable when you are moving the X and Y points of an SVG while also having the rotate: animation applied. If I am applying this animation to a stationary SVG, rotation works fine. Likewise the tracking code to move the cx and cy on my svg elements works fine without rotation applied. It is a combination of the two that seems to cause the problem.

 

Also oddly, it only seems to happen when the cx and cy are modified outside of GS, if using a GS animation to move the x and y, this behavior does not happen.  Unfortunately, I am using GS as part of a new GUI that is applying labels to 3d objects moving about in 3d space driven by BabylonJS, so a simple GS X and Y animation wont work. 

 

Expected Behavior: The SVG rotates around its center forever even if X any Y coordinates of the SVG were moved

 

Actual Behavior: The object does rotate, but it also seems to have added an unwanted behavior of orbiting the X and Y point where the gsap rotation animation was first called (not the X and Y I am sending it). The radius of the orbit increases as I move the SVG's X and Y further from the 2d spot where the gsap.to was first called, and orbit decays if it is moved closer, eventually lining back up perfect if the 3d animation returns to the exact spot where the gsap was first initialized.
image.png
(Example Above: A is close to where the rotation was first started, so its outer spinning shape is orbiting the center circle close, B is really far from where the animation was initially started so the piece being rotated has a massive super fast orbit, to the point where you rarely see it as a fragment wizzing by screen. C elements, never changed X or Y position so their callout shapes are positioned correctly around their inner circles)

 

This is my rotation command, its basic, and works until you start trying to move the cx and cy coordinates, then it exhibits the behavior above...

 

      gsap.to("#outer_" + i, {rotation: 360, transformOrigin:"center", ease: Power0.easeNone, repeat:-1, duration: 1, delay:0.5}); // base

 

 

This is my actual X and Y movement code. Its tracking a 3d object in a canvas and converting the X and Y to screen space to move SVG. This works fine until you start that animation above, then the behavior instantly starts manifesting as described in paragraph 1.

 // 2D Position the Nurkle Anchor Container
                         targetNurkle.computeWorldMatrix(true); //calculate 2d X and Y from a  Vector 3 (x,y,z)
                         let targetPostargetNurkle.getAbsolutePosition(); // get the 3d worldspace (x,y,z) of the object
                         let p = pwtHelp.calc2dPointFrom3d(targetPos); // converts a 3d point into a 2d X,Y screen space
                         let x2 = p.x;
                         let y2 = p.y;
                         let targetAnchorName = "nurkle_anchor_" + pwtNurkle.activeNurkles[i][0]; // build the target name
                         let targetAnchorID = document.getElementById(targetAnchorName); // find the SVG in the HTML
                         targetAnchorID!.setAttribute('cx'x2.toString()); // set anchor X coordinate  - HERE IS X BEING SET
                         targetAnchorID!.setAttribute('cy'y2.toString()); // set anchor Y Coordinate - HERE IS Y BEING SET

 

In looking at the transform spec you need to feed in rotation and a rotation, x, and y. 

transform="rotate(deg, cx, cy)"

 

I am assuming somehow I am messing with how GS is building that three part transform when you just feed it a rotation with GS and the X and Y is driven by the above. But I don't understand what exactly is happening / how this is manifesting what it is / what work arounds exist. 

I put everything up on a development domain and you can view this issue live here https://dern.tech/?n=30 . Watch the callout shapes and you will see part of them split off from the main shape tracking the 3d models. Unfortunately this bug is best seen on the site, it is a bit too complex to paste into a pastebin or codepin. I am actually having trouble describing it, as it seems so odd as nothing apparent is telling that rotation to orbit like it is, which is why I decided to link to the actual issue. If you sit there and watch it for a moment you will see what I am talking about. Note, the GS integration over the weekend broke a bunch of functionality in the site, so you can't really navigate or do much of anything but see the bug in action right now. that is not GS's fault I was re-writing some things moving from pure jquery animations for the 2D UI to gain more flexibility in doing things like animating SVG line.  So far, I am loving the GS library. 


Once the page loads, you should just see the error. Watching the drones move toward and away from their home positions makes the bug very apparent.

 

This whole project is only a month into development with absolutely tons to do. So, sort or embarrassed to link to the dev domain given its state being so far from feature complete and not pretty yet. But, this bugs got me stumped. Web development is not something I do often, in fact its been years. Likely, this is all a noob error. 

 

Thanks for any help you can offer. 

 

See the Pen bGBqLxw by Calsa128 (@Calsa128) on CodePen

Link to comment
Share on other sites

Hey Calsa. I think this is because that GSAP has to use the x and y of SVG to calculate the proper transforms as GSAP normalizes the transform origin of SVG elements for you. So you should set the value with GSAP instead. Just use a .set() instead of .setAttribute. For example:

gsap.set(targetAnchorID, { attr: { cx: x2.toString() } });

Here's how I'd format the JS in your CodePen (though I'd probably use ES6 let and consts instead of var):

See the Pen VwmbwWL?editors=0010 by GreenSock (@GreenSock) on CodePen

  • Like 1
Link to comment
Share on other sites

Hmm, I gave it a shot and it still exhibited the same strange behavior. 

It almost looks like the transform origin remains at the initial point where rotate is called, but the X and Y of the shape expands as the cx and cy property move further away from that fixed origin point.

 

I tried a few things to move the origin itself, but with a similar lack of success.  

 

How does GS generate its timelines? Is it all precomputed when you first call a to or fromTo? That  would then make sense that the origin remains the same as its not even looking for the X and Y update; or does GS dynamically generate the tween each frame as they are played? 

 

Link to comment
Share on other sites

2 hours ago, Calsa said:

How does GS generate its timelines? Is it all precomputed when you first call a to or fromTo?

Yes, it's pre-computed.

 

Updating the transform origin every tick is doable but would be a lot of extra work for most people who don't want to do that and it would likely be unexpected for most people. You also have to specify the order of which to do the transforms and updating of the transform origin. 

 

Perhaps if you made a demo that more closely represented the issue we could help more? 

  • Like 1
Link to comment
Share on other sites

In this Codepen, notice how green/ blue elements center_2 and outer_2 stay in formation, with outer_2 orbiting close to center_2 as their cx and cy are changed? This is what I expect to happen as they both share the same cx, cy points.

 

The red/black is the problem in this example. center_1 and outer_1 also both share a cx and cy point, but the outer_1 is not moving to the same position visually as center_1.  The only difference between green/blue, and red/black is that red/black is using a rotate on the outer_1 element:

 

gsap.to("#outer_1", {rotation: 360, transformOrigin:"center", ease: Power0.easeNone, repeat:-1, duration: 1, delay:0.5}); // base

 

See the Pen LYbyyeB by Calsa128 (@Calsa128) on CodePen


 

How do I still use the rotate, but not have the black outer_1 chunk move randomly about the screen, instead of doing what it should, which is spin around the black center_1 circle as blue/green is doing?

 

 

 

Link to comment
Share on other sites

3 hours ago, ZachSaucier said:

Why would you not use a <g> for grouping elements that you want to animate? It will perform better and be easier to manage.

See the Pen JjbNBeP by Calsa128 (@Calsa128) on CodePen


 

That seems to do it. I forgot about groups. Thanks.

 

Just as a followup Q before I mark solution, so this does not get buried and I don't learn from this.

 

Why was that not moving correctly before? Clearly it was a translation issue, but I don't understand why rotate was causing that to happen.

Link to comment
Share on other sites

1 hour ago, Calsa said:

Why was that not moving correctly before?

I thought we covered that earlier?

Quote

Updating the transform origin every tick is doable but would be a lot of extra work for most people who don't want to do that and it would likely be unexpected for most people. You also have to specify the order of which to do the transforms and updating of the transform origin. 

Or is your question different?

  • Like 2
Link to comment
Share on other sites

Quote

I thought we covered that earlier?

You answers what was going on under the hood, but not necessarly how to fix it from a user perspective in that initial post. Technically, I am still cheating a solution by wrapping it in a group, which would not be very intuitive if you only had one element like this example.

 

See the Pen gOLWqmE by Calsa128 (@Calsa128) on CodePen

 

Any time you apply rotate to a cx,cy coordinate set manipulated outside of GS it seems this behavior will happen (though I am a noob so maybe not?)

I may just be an oddball case as most people are likely keep all animation in GS. But IMO it seemed more unexpected that a position cx, cy would be thrown off so much when a simple rotation is applied, given how "smart and easy" the positioning of GS is suppose to be.  I still don't understand why positioning  was thrown off so much or what exactly it was even using for its position to move like it was.
 

Regardless, if wrapping things in groups gets the job done, good enough for me I suppose. Two days lost on this is enough. Thanks for the help.

Link to comment
Share on other sites

19 minutes ago, Calsa said:

I may just be an oddball case as most people are likely keep all animation in GS.

Definitely so :) 

 

19 minutes ago, Calsa said:

IMO it seemed more unexpected that a position cx, cy would be thrown off so much when a simple rotation is applied

I explained why this is...

 

If you can think of a better solution for how to normalize SVG transforms and transform origins we welcome it :) But I'd put my money on a better solution not being able to be found.

 

21 minutes ago, Calsa said:

Technically, I am still cheating a solution by wrapping it in a group, which would not be very intuitive if you only had one element like this example.

I would say "cheating" is a stretch. As for whether or not it's intuitive, it depends on how much you understand what's happening. I would say it's pretty intuitive to add an otherwise-empty group around the element in this case because at allows you to separate the transformOrigin from the element itself. 

 

Perhaps @GreenSock has something he wants to add?

  • Like 1
Link to comment
Share on other sites

3 minutes ago, ZachSaucier said:

If you can think of a better solution for how to normalize SVG transforms and transform origins we welcome it :) But I'd put my money on a better solution not being able to be found.

 

My solution was to stop trying to reinvent the wheel by animating everything in jquery / jscript, and buy GS 😛

 

Quote

I would say "cheating" is a stretch. As for whether or not it's intuitive, it depends on how much you understand what's happening. I would say it's pretty intuitive to add an otherwise-empty group around the element in this case because at allows you to separate the transformOrigin from the element itself. 

From a noob starting out, its not. Maybe to an admin it is :). Maybe add something to SVG tips to help others? I did RTFM first thing but could not find any sort of clue in there this needed to happen.

28 minutes ago, Calsa said:

I may just be an oddball case as most people are likely keep all animation in GS.

Quote

Definitely so :) 

The story of my life.... I do tend to find new and creative ways of breaking software. It is a gift. I think....

Link to comment
Share on other sites

A few quick notes: 

  1. It would be much, much cleaner to just animate transforms (x and y) instead of the "cx" and "cy" attributes. 
  2. It looks like your demo is animating some things with jQuery. I'd strongly recommend avoiding that - GSAP is literally up to 20x faster (and of course far more flexible, capable, and it solves cross-browser issues that jQuery doesn't)
  3. You can make your code more concise by using gsap.set(...) for all those element.setAttribute(...) calls you're doing
  4. There's a GSAP utility function that makes it easy to get a random number between two values

Here's a fork that shows some of those improvements: 

See the Pen 1a5fff43b1494403713b8f177863b358?editors=0010 by GreenSock (@GreenSock) on CodePen

 

I noticed that you're animating TWO elements in each case (outer and center) to the identical x/y values - this is an excellent case for what @ZachSaucier recommended with just wrapping those in a <g> so you can animate one thing and it'd affect both. 

 

I can give you a long explanation about the whole transformOrigin thing and why your old demo is behaving that way, but I figured it'd be better to just explain a cleaner approach and then if you want the explanation for the old behavior, just ask. :)

 

Happy tweening!

  • Like 4
Link to comment
Share on other sites

5 hours ago, GreenSock said:

A few quick notes: 

  1. It would be much, much cleaner to just animate transforms (x and y) instead of the "cx" and "cy" attributes. 
  2. It looks like your demo is animating some things with jQuery. I'd strongly recommend avoiding that - GSAP is literally up to 20x faster (and of course far more flexible, capable, and it solves cross-browser issues that jQuery doesn't)
  3. You can make your code more concise by using gsap.set(...) for all those element.setAttribute(...) calls you're doing
  4. There's a GSAP utility function that makes it easy to get a random number between two values

Here's a fork that shows some of those improvements: 

 

 

 

I noticed that you're animating TWO elements in each case (outer and center) to the identical x/y values - this is an excellent case for what @ZachSaucier recommended with just wrapping those in a <g> so you can animate one thing and it'd affect both. 

 

I can give you a long explanation about the whole transformOrigin thing and why your old demo is behaving that way, but I figured it'd be better to just explain a cleaner approach and then if you want the explanation for the old behavior, just ask. :)

 

Happy tweening!

Thanks for the example, comments, and product.

 

Though, I was using jquery for positioning as if you set your X and Y inside GS (this was actually done way up top) the issue does not manifest. It is  only if position data is set outside of GS does the issue become noticeable. Jquery was the quickest, and most easy way to simulate what my site is doing, so you could see the issue.

At any rate, were all good here. Thanks for the awesome product.

  • Like 1
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.
×
×
  • Create New...