Jump to content


Feature request: implement animate viewBox in MorphSVGPlugin

Go to solution Solved by GreenSock,

Recommended Posts

Hey there!


A lot of the times, when morphing SVG, the size of SVGs changed and when in a fixed sized container, the viewBox may needs to be changed.


Check my pen code, where you can see two morphing letters.


So I created a animateSvgViewBox function to also animate the viewBox, but it becomes kind of jerky, especially when used with many elements.


So: feature request -> implement animate viewBox in MorphSVGPlugin.


Or ist there a better option to do that?

See the Pen xxaOYqE by maki3000 (@maki3000) on CodePen

Link to comment
Share on other sites

For these sorts of things, I usually just use: overflow: visible!important; on my SVG elements. It'd be a tough ask to try to figure out how to constrain/contain shapes in the viewBox automatically (does the height change, with the width, etc). Also, animating the viewbox can have some performance implications too.

  • Like 5
Link to comment
Share on other sites


I tried to do, what I did with my viewBox animation with CSS, but I failed:

See the Pen PodzaZQ by maki3000 (@maki3000) on CodePen


I guess, MorphSVGPlugin somehow uses CSS transform for it's animation as well, so this one doesn't work. Or is there another reason?

Link to comment
Share on other sites

  • Solution
42 minutes ago, maki3000 said:

I guess, MorphSVGPlugin somehow uses CSS transform for it's animation as well, so this one doesn't work. Or is there another reason?

No, it only affects the path data. 


Never ever apply CSS transitions to anything that JavaScript is animating because not only is it horrible for performance, but it'll prolong all the effects because whenever JavaScript updates a value (which GSAP typically does 60 times per second), the CSS transitions interrupt and say "NOPE! I won't allow that value to be changed right now...instead, I'm gonna slowly make it change over time". So it disrupts things, adds a bunch of load to the CPU because you've now got CSS and JS both fighting over the same properties, but let's say you've got a 1-second tween and the CSS transitions are set to take 1000ms...that means what you intended to take 1 second will actually take 2 seconds to complete. 


You could probably just apply an x or y value to your tween to compensate for any shift. That'd be the simplest. 


Actually, the best thing is to just make your SVG artwork correct to begin with, as @PointC's article says, artwork prep is key. 


If your goal is to just prevent the SVG element from being outside the <svg> container and you want to automate that (rather than calculating it yourself), so that if it bleeds 20px off the left side, it moves 20px to the right, here's a relatively simple helper function that'll just apply an "x" or "y" transform to correct for any bleed off the edge: 

function keepInViewBox(tween) {
  let progress = tween.progress(); // record progress so we can revert it later
  tween.progress(1, true); // jump to the end so we can measure the end state
  let targetBounds = tween.targets().map(target => target.getBoundingClientRect()), // get an Array of all the target bounds
      svgBounds = tween.targets().map(target => (target.ownerSVGElement || target.parentNode).getBoundingClientRect()); // get an Array of all the SVG bounds
  tween.vars.x = i => {
    let left = svgBounds[i].left - targetBounds[i].left,
        right = svgBounds[i].right - targetBounds[i].right;
    return left > 0 ? left : right < 0 ? right : "+=0";
  tween.vars.y = i => {
    let top = svgBounds[i].top - targetBounds[i].top,
        bottom = svgBounds[i].bottom - targetBounds[i].bottom;
    return top > 0 ? top : bottom < 0 ? bottom : "+=0";
  tween.invalidate().progress(progress, true);
  return tween;

And here it is in action: 

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


You just feed it the tween and it'll handle the rest. 


It doesn't do advanced things like scaling or centering, nor does it actually change the viewBox. I think in most cases, those things are probably unnecessary. 


I looked at your demo and I would definitely not do that setInterval() stuff to animate the viewBox. It's terribly inefficient and it's not synchronized with repaints and you get almost no control over easing, etc. It's far better to leverage the power of GSAP to do all that. Way more efficient, synced, and simple. 


I hope this helps!

  • Like 4
Link to comment
Share on other sites

Just bumping @elegantseagulls response again though as overflow:visible is what I do too. Simple is good!



For these sorts of things, I usually just use: overflow: visible!important; on my SVG elements. 


  • Like 3
Link to comment
Share on other sites

Hey! Thanks to all of you! And thank you very much @Jack for your detailed explanations. This is very helpful 👍


Based on Jacks CodePen, I added scaling, because I also have a letter morphing from "K" to "I" and because I scale, I decided to have transformOrigin: 'center'


I also added containers around my SVGs, so I can CSS transform their widths and have a container with letters that doesn't change in size.


Here we go:

See the Pen oNPzgqN by maki3000 (@maki3000) on CodePen


One and a half day later I'm pretty happy with the result 😀

Link to comment
Share on other sites


I checked my code again and found out that my scale x-positioning didn't work all the time. Here's a new version of my pen:

See the Pen bGxBNBe by maki3000 (@maki3000) on CodePen


Also, I decided to use the overflow: visible!important;, because I like the result better, when it's overflowing and I do the width animations now also with a tween (for simplicity it don't use timeline, because I want to be the keepInViewBox as it is).

Edited by maki3000
Forgot to mention that I now use tween for animating the container widths.
Link to comment
Share on other sites

Heya! Thanks for popping back in. Sorry to be the bearer of bad news, but on my very speedy mac, in chrome the animation is very jerky.

Animating margins and width is a pretty bad plan for performance, same with the viewport.  If you have overflow:visible set it seems a little redundant anyway.

Something like this may be a little better?

See the Pen e0fe8d0822c65a85567af4eb79617764?editors=0010 by cassie-codes (@cassie-codes) on CodePen


  • Like 3
Link to comment
Share on other sites

Yes, I'd definitely recommend Cassie's approach and just to be clear, that jittery behavior isn't necessarily about performance (though transforms definitely perform better), it can be just the nature of layout shifting around when you're altering width/height/margins especially when you've got things centered. Browsers often apply some kind of rounding to snap to the pixel grid, so imagine you've got a 100px-wide element that's centered in a browser window that's 201px wide. Doh! That lands on a half-pixel, 100.5. How is it supposed to render that in the grid of pixels? Perhaps it rounds it to 101. If you're animating the width and it's 99px, half on each side of 101 is gonna be 49.5. Doh! Anyway, I'm sure you see the point - it can lead to some bouncy behavior as the browser applies its rounding strategies to align those pixels to the grid. Transforms can handle sub-pixel rendering.

  • Like 3
Link to comment
Share on other sites

Hey! Thanks a lot!


@Cassie this looks great 👍

I first thought, I need it to be responsive, but I guess, I will have it the same size for all screens, so working with fixed values and the timeline is for sure far a better solution. And when using overflow: visible, I don't need to care about viewBox size and just move the letters where I want them to be.

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.