Jump to content
Search Community

drawSVG not recalculating path length during tween

evomedia.lt test
Moderator Tag

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

Hello everyone!

I'm trying to make a responsive website utilising svg animations, however I noticed, that when using drawSVG tweens, the length of a path gets only calculated once, during the initialization of a tween.

 

My question: Is there a way to trigger path length recalculations during the drawSVG tween without stoping/restarting it.

 

Attached to this thread is a codepen illustrating the problem. Top path is what it's supposed to look like (yellow path hitting the red dot on extremes), and lower path shows what I'm getting.

 

Thank you in advance

 

See the Pen KBjLLJ by driezis (@driezis) on CodePen

Link to comment
Share on other sites

At this point I came up with a workaround, but I don't think it's a proper solution:

See the Pen XBvZVG by driezis (@driezis) on CodePen

 

What I'm doing here is animating a control object with percentage values, and then using onUpdate call to set drawSVG at the given time. Don't know how efficient it is speed-wise, but I am certain this is a workaround and not a solution. What I mean is if I have one or ten objects it's passable, but if I have a hundred animating differently at a time it becomes hard to keep track. Also not all objects require path recalc on every frame, or none at all.

 

I feel like there should be some sort of a flag in drawSVG for the tween to update path length on every frame or even an event callback to resize of the window, because that's when geometry usually tends to change.

 

Maybe I'm thinking too hard about this... Any opinions?

Link to comment
Share on other sites

Interesting. Yeah, that's a pretty uncommon need, but I took a crack at accommodating it with an updated DrawSVGPlugin, so you could just add "live" to your drawSVG value and it'll keep the length updated (live). Like drawSVG:"0% 100% live". If you've got other ideas for a better API, let me know. Here's a fork of your original: 

 

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

 

I have NOT updated the official version of the plugin yet - just the codepen-only one. Let me know if it looks good to you. 

 

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

2 hours ago, GreenSock said:

Interesting. Yeah, that's a pretty uncommon need, but I took a crack at accommodating it with an updated DrawSVGPlugin, so you could just add "live" to your drawSVG value and it'll keep the length updated (live). Like drawSVG:"0% 100% live". If you've got other ideas for a better API, let me know. Here's a fork of your original: 

 

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

 

I have NOT updated the official version of the plugin yet - just the codepen-only one. Let me know if it looks good to you. 

 

 

This is awesome! It does what I want in general, even though you can still notice some drift from the center when the tween is restarting (when the line gets long again). Maybe because the line length changes on every frame (it's own tween), and TweenMax optimizes the frame updates for the svg somehow? Doesn't matter, this will be enough for what I need.

 

Now for suggestions, I haven't tested it, but recalculating many objects lengths live should be using extra resources, and sometimes what you really need is change only when the browser window resizes. Maybe there could be a "onResize" along with "live" somehow? Or it could even be a default behaviour, because it is important for responsive aspect of dynamic svg graphics. So possible solution would be just to slap an onResize event listener any time drawSVG tween starts, and recalculate path length if it triggers...

 

Maybe my case is an edge case, but I think animated SVG UI's is the future, and I'm trying to make it as dynamic and responsive as possible.

  • Like 1
Link to comment
Share on other sites

I see what you mean regarding performance, but I think it'd be super awkward to deliver what you're asking for with the "only on window resize" thing. The reality is that resizes can happen at other times too (not just window resizes) and I don't see a clean way to listen for (or give a hook to call) all kinds of resizes. GSAP is build with GC in mind, so we can't set up an internal cache that holds references to all DrawSVGPlugin instances (that'd prevent proper GC). And there's not a way to grab the instance from a tween either. So unless I'm missing something, it's just not realistic to offer this kind of functionality. 

 

If you're really concerned about performance and only running the length recalculations at specific times (like window resize), perhaps you could leverage invalidate(), like:

 

var tween = TweenMax.to(".path", 2, {drawSVG:"0%"});
    
function onResize() {
    var p = tween.progress();
    //forces the tween back to the start, then invalidates it so that it'll re-run its calculations when jump to the recorded progress, making it appear seamless.
    tween.time(0).invalidate().progress(p);
}

 

Of course you could keep your tweens in an array and loop through them, or use TweenMax.getTweensOf() or TweenMax.getAllTweens(). 

 

Does that help? 

 

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

Couple of days ago I was working on something out of personal interest. I didn't have any plans to post it in the forum, it was meant to be a PM to @GreenSock

 

I haven't spent more than couple of hours on it so it is not complete and I haven't tested it for performance. Few days ago there was a question about using drawSVG for canvas, so I wrote little class as experiment that will do it. Today I saw your question and I modified it to support path modification.

 

See the Pen xJvNWo?editors=0010 by Sahil89 (@Sahil89) on CodePen

 

Bonus: You can use MorphSVG on it.

 

See the Pen bjPmyx?editors=0010 by Sahil89 (@Sahil89) on CodePen

 

Though all browsers don't support Path2D so you will need pollyfill for that. See if you can use it. Also, what I am doing is very little and I have too much freedom to do it, while GSAP is really big library and every change has to be considered for any side effects and if enough users need it.

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

On 8/18/2018 at 9:06 AM, GreenSock said:

I see what you mean regarding performance, but I think it'd be super awkward to deliver what you're asking for with the "only on window resize" thing. The reality is that resizes can happen at other times too (not just window resizes) and I don't see a clean way to listen for (or give a hook to call) all kinds of resizes. GSAP is build with GC in mind, so we can't set up an internal cache that holds references to all DrawSVGPlugin instances (that'd prevent proper GC). And there's not a way to grab the instance from a tween either. So unless I'm missing something, it's just not realistic to offer this kind of functionality. 

 

If you're really concerned about performance and only running the length recalculations at specific times (like window resize), perhaps you could leverage invalidate(), like:

 


var tween = TweenMax.to(".path", 2, {drawSVG:"0%"});
    
function onResize() {
    var p = tween.progress();
    //forces the tween back to the start, then invalidates it so that it'll re-run its calculations when jump to the recorded progress, making it appear seamless.
    tween.time(0).invalidate().progress(p);
}

 

Of course you could keep your tweens in an array and loop through them, or use TweenMax.getTweensOf() or TweenMax.getAllTweens(). 

 

Does that help? 

 

 

I didn't know about this. Thank you, Jack! It works when the tween is one sided, as in it's a one time or a looped tween. However, if I do a yoyo:true, progress goes from 0 to 1, then back to 0, thus making this solution nonfunctional. What I mean is if I invalidate the tween on it's way back, instead of continuing with the animation it starts going forward again. I made a codepen example here: 

See the Pen wEwJoL by driezis (@driezis) on CodePen

 (try changing window width when animation goes forward and then backward, you will see that when you change it on the way back, it jumps to forward animation). Maybe when yoyo is true, the progress should count to 0.5 when going forward and 0.5 to 1 when going back?

 

 

21 hours ago, Sahil said:

Couple of days ago I was working on something out of personal interest. I didn't have any plans to post it in the forum, it was meant to be a PM to @GreenSock

 

I haven't spent more than couple of hours on it so it is not complete and I haven't tested it for performance. Few days ago there was a question about using drawSVG for canvas, so I wrote little class as experiment that will do it. Today I saw your question and I modified it to support path modification.

...

Bonus: You can use MorphSVG on it.

...

 

Though all browsers don't support Path2D so you will need pollyfill for that. See if you can use it. Also, what I am doing is very little and I have too much freedom to do it, while GSAP is really big library and every change has to be considered for any side effects and if enough users need it.

 

Sahil, this is great stuff! However as you mentioned, Path2D isn't fully supported everywhere, specially still behind on mobile devices. Also, it goes back to using raster graphics where we have to worry about scalability, and I like how crispy svg vectors look on any display/size.

 

 

Offtopic question: how do you embed a codepen in a forum reply? :)

Link to comment
Share on other sites

As @GreenSock already had provided you solution, the only reason I posted that because you mentioned you wanted to recalculate values on resize. But svgs are scalable so resizing won't really change values unless you recalculate your paths manually so I thought you could just do same thing in canvas instead. But of course you know what fits in your requirements.

 

Quote

Sahil, this is great stuff! However as you mentioned, Path2D isn't fully supported everywhere, specially still behind on mobile devices. Also, it goes back to using raster graphics where we have to worry about scalability, and I like how crispy svg vectors look on any display/size. 

 

Thanks. Just to clarify, canvas draws paths just like svg, just you have to do all the stuff manually. You can also support different pixel density, so raster etc isn't really an issue. Ya the Path2D is not supported by all browsers, but the pollyfill works in IE 9 as well without any significant performance issues.

 

 

Quote

Offtopic question: how do you embed a codepen in a forum reply? :)

 

If you paste the url while in editor mode then it will get embedded.

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

14 hours ago, evomedia.lt said:

Also, it goes back to using raster graphics where we have to worry about scalability, and I like how crispy svg vectors look on any display/size.

 

A common claim about canvas is that it doesn't use vectors, to which I say, "O RLY?"

 

See the Pen WzbKLQ by osublake (@osublake) on CodePen

 

 

And Path2D is supported in modern browsers, which includes mobile. Edge does have a bug for SVG paths...

https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8438884/

 

But there are workarounds, including a polyfill. I just found out about this one, which was posted in that Edge bug.

https://github.com/goessner/parseSvgPathData

 

Just clearing up some misconceptions. @Sahil said it best about using what fits with your requirements.

 

  • Like 5
Link to comment
Share on other sites

5 hours ago, OSUblake said:

 

A common claim about canvas is that it doesn't use vectors, to which I say, "O RLY?"

...

 

And Path2D is supported in modern browsers, which includes mobile. Edge does have a bug for SVG paths...

https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8438884/

 

But there are workarounds, including a polyfill. I just found out about this one, which was posted in that Edge bug.

https://github.com/goessner/parseSvgPathData

 

Just clearing up some misconceptions. @Sahil said it best about using what fits with your requirements.

 

 

It's a great example, thank you OSUblake. One more thing about svg that still wins over canvas for me is that it's a part of the DOM, not only every element can be styled and animated in real time by CSS, it also can be interacted with, and supports selectors like :hover. With canvas I would need to fall back to tracking coordinates of each item (unless I don't know something about Path2D approach).

 

 

Back to my previous questions. Jack gave me this workaround:

On 8/18/2018 at 9:06 AM, GreenSock said:

...


var tween = TweenMax.to(".path", 2, {drawSVG:"0%"});
    
function onResize() {
    var p = tween.progress();
    //forces the tween back to the start, then invalidates it so that it'll re-run its calculations when jump to the recorded progress, making it appear seamless.
    tween.time(0).invalidate().progress(p);
}

...

 

However, I noticed, it doesn't work as intended on tweens that have yoyo:true. I'd like to know if that's a bug or intended behaviour.

Here's my codepen to illustrate:

 

See the Pen wEwJoL by driezis (@driezis) on CodePen

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, evomedia.lt said:

It's a great example, thank you OSUblake. One more thing about svg that still wins over canvas for me is that it's a part of the DOM, not only every element can be styled and animated in real time by CSS, it also can be interacted with, and supports selectors like :hover. With canvas I would need to fall back to tracking coordinates of each item (unless I don't know something about Path2D approach).

 

You typically redraw everything on every frame with canvas, so changing the color of a circle would be as simple as this.

 

var myCircle = {
  x: 100,
  y: 100,
  radius: 25,
  fill: "blue"
};

// it's going to be red on the next frame
myCircle.fill = "red";

 

 

And you have to keep track of the coordinates in order to tell the canvas where to draw your objects. But again, it's about using what's best for the job. For a good comparison between the DOM and canvas, check out this video.

 

 

 

 

 

1 hour ago, evomedia.lt said:

However, I noticed, it doesn't work as intended on tweens that have yoyo:true. I'd like to know if that's a bug or intended behaviour.

 

 

Progress just goes to 1, so it's hard to tell when it's playing backwards. Try using something like .totalTime() and then use the remainder operator with the duration times 2.

 

// Use 6 since the regular duration is 3
var totalTime = tween.totalTime() % 6;
tween.time(0).invalidate().totalTime(totalTime);

 

 

See the Pen qMWLYo by osublake (@osublake) on CodePen

 

 

 

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

14 hours ago, OSUblake said:

Not sure if the remainder operation is really necessary. I just did that to reset the totalTime so it's not a huge value. This should also work.


var totalTime = tween.totalTime();
tween.time(0).invalidate().totalTime(totalTime);

 

 

Thank you for the workaround, OSUblake :) It does what I need.

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