SammyC123

Animate SVG Object Along Motion Path

Recommended Posts

Using the standard animateMotion, it is very straightforward how one is to go about animating an object along a motion path. Looking at the documentation for GreenSock, however, makes this simple task seem highly confusing.

 

Is there a basic example or CodePen anywhere of how to go about animating an object along a motion path?

Share this post


Link to post
Share on other sites

No problem:

http://codepen.io/anon/pen/VerMWX

 

Keep in mind:

  1. TweenMax contains BezierPlugin
  2. BezierPlugin is much, much more flexible than the <animateMotion> SMIL thing which is probably why it seems more complex. It can handle multiple types of beziers (cubic or quadratic) or it can even plot a smooth bezier through a set of points (anchors) that you provide, and it can rotate the element in the direction of the path (autoRotate:true). It's so flexible, in fact, that it can animate non-positional data. It's not limited to x/y data. It can literally animate ANY numeric property. But x/y are the most common, of course, and that's what we're seeing in this example. 
  3. The reason MorphSVGPlugin is necessary is simply because it contains all the code to parse entire SVG paths and convert them to cubic bezier data, so even though we're not morphing anything here, MorphSVGPlugin's power is being leveraged. We could, of course, dump all that code directly into BezierPlugin but that'd likely double its size. It seemed cleaner to keep BezierPlugin relatively compact and let MorphSVGPlugin do the heavy-lifting on the SVG parsing. 
  4. MorphSVGPlugin is a membership benefit of Club GreenSock, so it's not in the public github repo or the CDN. 

The docs are here: http://greensock.com/docs/#/HTML5/GSAP/Plugins/MorphSVGPlugin/pathDataToBezier/

There's a YouTube video explaining it here: 

 

Does that answer your question? 

  • Like 6

Share this post


Link to post
Share on other sites

Fantastic! Exactly what I was looking for. You saved me hours of digging through docs. Thanks very much.

  • Like 1

Share this post


Link to post
Share on other sites

I'm also looking for an alternative to SMIL animateMotion. Is there a way to achieve the finer grained control of animation timing that the keyPoints/keyTimes mechanism provides?

 

What I need to achieve, is let the user draw a path, record it and play back the drawing at the exact speed it was drawn. This is fairly trivial to present with SMIL animateMotion.

Share this post


Link to post
Share on other sites

The good thing about what Jack posted above is that GSAP will make your animation work cross browser! Whereas SMIL animation is not supported in any Microsoft browser (IE and Edge). SMIL animations perform inconsistent or sometimes sluggish in Chrome, Safari, and Firefox.

 

http://caniuse.com/#feat=svg-smil

 

Also some other limitations using SMIL animations:

  • Partial support in older Safari versions refers to not working in HTML files or CSS background images.
  • As of Chrome 45 & Opera 32 SMIL is deprecated and usage will result in a warning in the console. Support is expected to be dropped in some future version.

So in Google Chrome SMIL is deprecated as of version 45, and Safari has partial support. Leaving GSAP as the only contender to animate SVG cross browser!

 

:)

  • Like 1

Share this post


Link to post
Share on other sites

Hi Jameson,

 

You could use DrawSVG to handle the animation. To draw paths, I like to use Draggable because of the way it handles nested transforms, mouse and touch events, and event throttling. If you need path smoothing, here's two different ways you can do that.

 

Catmull-Rom - faster, but shows more imperfections.

http://codepen.io/osublake/pen/BowJed?editors=0010

 

Bezier Interpolation - slower, but looks smoother.

http://codepen.io/osublake/pen/LGMexY?editors=0010http://codepen.io/osublake/pen/LGMexY?editors=0010

 

Here's a demo of timed path drawing using DrawSVG and Draggable...

http://codepen.io/osublake/pen/dGwZBx/

 


 

@@GreenSock

For the DrawSVGPlugin, it's really annoying having to convert this...

<polygon points="100,200,300,400,500,600" />

To this...

<polygon points="100,200 300,400 500,600" />

Can you confirm that you can't use an array of points for polygons/polylines? Like everything in the SVG spec, the only people that can understand it are the people that wrote it. I haven't come across an example of it not working.

  • Like 3

Share this post


Link to post
Share on other sites

Much better!

 

Could you add support for arrays to the MorphSVGPlugin? I know it's not that hard to convert to a string, but it makes things simpler. So someting like this...

var points = [100,200,300,400,500,600];
var data = ["M",100,200,"L",300,400,500,600];

// From this
TweenLite.to(polyline, 1, { morphSVG: points.toString() });
TweenLite.to(path, 1, { morphSVG: data.join(" ") });

// To this
TweenLite.to(poly, 1, { morphSVG: points });
TweenLite.to(path, 1, { morphSVG: data });

Share this post


Link to post
Share on other sites

Hm, I'm not sure about that one Blake - I totally see why you'd ask about that, but I'm a bit concerned about:

  1. Sensing correctly when it's an array that should be joined like that - what if someone passes in a jQuery object that's the target shape? That'd kinda look like an array and we can't do (if obj instanceof Array) because that breaks in a multi-window environment. 
  2. I'm sure I could put together enough conditions to pretty accurately determine when it's an array with the proper data and do what you're asking (check the contents of the array and look for numbers and/or strings), but I'm not sure it's worth the extra kb and processing time for something that's so simple to do externally as you showed above. 
  3. Again, it's dead-simple to do manually. If it requires 8x as much code internally, is that delivering much benefit? 

I'm still open to that possibility, but I'm just sharing why I'm reluctant at this point. Feel free to offer other thoughts.

Share this post


Link to post
Share on other sites

Yeah, maybe it doesn't add much value if it complicates things.

 

As a side note about determining if something is an array. Unless you want to shim IE8, you don't need to use duck typing, instance, or toString checks anymore. You're pretty well covered using Array.isArray. It solves the multi-window environment issue, and works in all modern browsers.

 

Compatibility

http://kangax.github.io/compat-table/es5/#test-Array_methods_Array.isArray

  • Like 1

Share this post


Link to post
Share on other sites

Thanks for the info, Blake. And yeah, I think in the next major release, we'll strongly consider dropping support for IE8 and earlier. 

  • Like 1

Share this post


Link to post
Share on other sites

pathDataToBezier is great! However, is there any way to animate to a percentage of the length of the bezier path?

Share this post


Link to post
Share on other sites

Sure, you could just create a tween that animates along the entire path with Linear ease, pause() it, and then tween its progress to whatever percentage you want. Pseudo code: 

var bezierTween = TweenLite.to(..., {bezier:{...}, ease:Linear.easeNone}).pause();
var percent = 0.6; //let's animate to 60%...replace this with whatever you want.
TweenLite.to(bezierTween, percent * bezierTween.duration(), {progress:percent});

You can apply whatever ease you want to that last tween. 

 

So you're basically using one tween to animate the "progress" value of another tween. Mind-bending, right? ;)

  • Like 3

Share this post


Link to post
Share on other sites

Cheers Jack, and Carl for the pen. Mind blown! Starting to feel a little like Inception. A dream within a dream. Or should I say 'A tween within a tween'? Boom! ;-)

  • Like 3

Share this post


Link to post
Share on other sites

Can this also be done when using TimelineMax?

Share this post


Link to post
Share on other sites

Absolutely. TimelineMax is basically a container for tweens, and it extends the same base class so that it has all the same core methods for maximum flexibility (like progress(), seek(), play(), pause(), reverse(), timeScale(), etc.)

  • Like 1

Share this post


Link to post
Share on other sites

hmm, I'm really not sure what you mean. 

It really helps to provide a reduced demo with just code related to what needs solving.

 

Jack, was describing that a TimelineMax also has a progress() method that can be animated.

 

I'm sort of guessing that you are asking to animate progress() of the animation that moves the circles.

 

Maybe something like this: 

var tl = new TimelineMax({repeat:-1, repeatDelay:2});
var baseTime = 0.7;
var percs = [53, 60, 66, 85, 100];
var linesPerc = ["0% "+percs[0]+"%", "0% "+percs[1]+"%", "0% "+percs[2]+"%", "0% "+percs[3]+"%", "0% "+percs[4]+"%"];




var bezTimeline = new TimelineLite({paused:true});
bezTimeline.staggerTo(".circ", 2, {
  cycle: {
    bezier: function(i) { 
      var line = ".line"+[i];
      var circ = ".circ"+[i];
      var motionPath = MorphSVGPlugin.pathDataToBezier(line, {align:circ});
      return {values:motionPath, type:"cubic"};
    }
  },
  ease:Power4.easeOut
}, 0.15);

tl.fromTo('#center-circ', baseTime*1, { scale: 0 }, { 
    scale: 1,
    delay: 0.5,
    ease: Elastic.easeOut.config(0.5, 0.4)
}, 0.15)

//control the progress of the bezierTimeline?
tl.to(bezTimeline, 1, {progress:0.2, ease:Back.easeOut})
  .to(bezTimeline, 1, {progress:0.4, ease:Back.easeOut}, "+=1")
  .to(bezTimeline, 1, {progress:1}, "+=1")

http://codepen.io/GreenSock/pen/PzKadL?editors=0010

 

If not, please try to be more specific about the effect you are trying to achieve. Thanks.

Share this post


Link to post
Share on other sites

I'm trying to get the dots to rotate round with the ends of the white lines at the same time as they draw out, as if they are stuck to the ends of the lines.

Share this post


Link to post
Share on other sites

hmm, it sounds like you need to control the progress of individual tweens. Instead of using the staggerTo and cycle to generate the circle-along-a-motion path tweens you should make them all separately outside the timeline and then inside your timeline create tween's that animate the progress of each when each line starts animating with DrawSVG. 

 

If it were me I'd back away from using stagger for each effect. Create a loop that does the animation for 1 circle and 1 line in each iteration. Will make it easier to put each animation in the main timeline at the right time. 

Share this post


Link to post
Share on other sites

Cheers Carl, I'll give that a blast later. Really appreciate your help guys!! Awesome!

Share this post


Link to post
Share on other sites

Very nice! Glad you got it working. Thanks for the update.

Share this post


Link to post
Share on other sites

Hey Jack and Carl,

 

Just thought I'd show you the final live result for the project I was using this in: http://murraytweetindex.ie/

 

Thanks for the great support once again!

 

Pete (aka Barman) ;-)

 

Also, I've got all the main animations in pens if they are of help to anyone else:

 

http://codepen.io/petebarr/

 

http://codepen.io/petebarr/pen/LkLpYq

 

http://codepen.io/petebarr/pen/YWLjQW

 

http://codepen.io/petebarr/pen/WxyvqO

  • Like 5

Share this post


Link to post
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.