Jump to content
Search Community

Animating nested SVGs

euan.cowie test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hi there!

 

I'm working on a React project and am using GSAP for my animations. The animations I'm performing are on SVGs using the timeline. The problem I've run in to is that I'm unable to perform the animations (.to() transforms) on <svg> elements themselves. This seems to part of the specification: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform. My code contains an outermost <svg> with nested <svg> elements within. It is these nested SVGs I am trying to animate. NB. The reason I need to use nested SVGs is that I want to keep relative positioning of their child elements and not have to programmatically maintain position of each child element...

 

However, things work perfectly fine if I wrap each of these nested SVGs with a <g> and perform the GSAP timeline.to() calls on those elements. I feel like this is a cop out and leaves the semantics of the XML much to be desired (it doesn't look good if each group only ever has a singular child).

 

I'm wondering if I'm missing something in the Greensock API or maybe even perhaps something fundamental with regards to the SVG spec. I've attached a simple codepen to give an idea of what I'm on about :)

 

I appreciate any help offered! Thank you!

Euan 

 

See the Pen YzGqvOy by euan-cowie (@euan-cowie) on CodePen

Link to comment
Share on other sites

  • Solution

If I understand your question correctly, the problem is that you're actually animating transforms but you're intending to animate "x" and "y" ATTRIBUTES (not transforms). So your #svg-player initially has no transforms applied, but it has an "x" attribute of 120, and "y" attribute of 80. In GSAP, animating transforms is probably the most common thing people do, so "x" and "y" are aliases for transform: translateX() and transform: translateY() CSS values. In your animation, you're animating x and y (transforms) to 0...but they're ALREADY at 0 (no transforms). If you want to animate the attributes, use the AttrPlugin which is already built into the core, so just wrap your values in attr:{}: 

// BAD: 
timeline.to("#svg-player", { x: 0, y: 0, duration: 2 }, 2);

// GOOD: 
timeline.to("#svg-player", { attr:{x: 0, y: 0}, duration: 2 }, 2);

Also, for the record, I've seen browsers behave oddly when you try to nest SVGs like that. I can't remember exactly what happened, but I believe it had something to do with ignoring transforms on the nested SVGs. So use at your own risk - it has nothing to do with GSAP at all; it's just a browser thing. 

 

Happy tweening!

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Thanks for the feedback! You were, of course, right on the money $$$. I was completely unaware of the "attr" var and the AttrPlugin. I'll definitely take your experience of nested SVGs in to account and do a little experimenting. I'm just not aware at this time of any other method as convenient when it comes to keeping elements neatly positioned in their respective "containers".

 

Thanks again!

Link to comment
Share on other sites

On 12/6/2020 at 8:03 AM, euan.cowie said:

However, things work perfectly fine if I wrap each of these nested SVGs with a <g> and perform the GSAP timeline.to() calls on those elements. I feel like this is a cop out and leaves the semantics of the XML much to be desired (it doesn't look good if each group only ever has a singular child).

I didn't quite follow the objection there. How is wrapping it all in <svg> somehow "better" than <g>?  If I understood your goal correctly, I'd personally opt for using a <g> for sure. 

 

Glad you got things working. 👍

  • Like 3
Link to comment
Share on other sites

Yeah -  I was just typing as Jack responded, but I can't really think of a good reason not to use a group tag. Nested SVGs are just too problematic and groups are made for... well... groups.

 

You can often create everything at 0,0 and then positioning things is super easy. Just my two cents. YMMV. :)

 

  • Like 3
Link to comment
Share on other sites

I see! I had another look at what I was doing. With the <g> elements I can no longer position my "Player(s)" by x,y coordinates as attributes but I can still use a transform attribute.

 

I've made a new codepen, here: 

 

See the Pen ExgyKLO by euan-cowie (@euan-cowie) on CodePen

 

Would this be the best way of creating them (w/o <svg> nesting, w/ <g> "grouping") then? This updated codepen uses React to show what I'm really trying to get at - from a hierarchical POV. This codepen also shows the current problem I'm working on - which is how to update the rectangle above the "Player" <circle> with the current position of the player while animating. My presumption is to use the onUpdate var of timeline.to() but I don't know the best way to pass this information from a parent component to a child component without breaking encapsulation. This is more a question for a React forum or something though...

 

Thanks for all your help. It's been very informative!

 

 

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...