1

How to animate along an SVG path at the same time the path animates?

 1 year ago
source link: https://benfrain.com/how-to-animate-along-an-svg-path-at-the-same-time-the-path-animates/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to animate along an SVG path at the same time the path animates?

21.12.2022 0 comments
1 days since last revision. Content should be accurate.

I had to create something along these lines (pun intended) recently; a line that animates to nothing with ‘something’ on the end; as an exercise of the reader, maybe you can make it the fuse on a bomb, as it burns towards the explosion, ‘Wile E. Coyete’ style!

To the best of my knowledge, Polygon magazine pioneered the SVG line drawing technique around a decade ago. You have an SVG path, and ‘draw’ the line along that path with the help of a couple of SVG stroke properties. Whoever first figured that technique out deserves our thanks because it is a hack, and yet is used everywhere to this day, to great effect.

So, making the receding line is a solved problem, but adding something else, to follow that same path, takes a little more work. I ended up finding two different ways to do this. One uses SVG, the other uses CSS offset-path. We will cover both.

So, to set the stage. Here is where we are starting our journey:

Click the button and the line moves but the circle stays where it is. We want the circle following the line!

The SVG way

It never ceases to amaze me how much capability is built into the XML markup of SVG. Turns out, you can nest an animateMotion element, inside the element you want to move, and give it the same path to follow. If your current element is self closing, change that to be open and nest in the new element like this:

<circle r="7.4" fill="#979797">
    <animateMotion dur="10s" repeatCount="1" fill="freeze" path="M7 53 2 23C1 11 10-1 22 0c14 1 29 20 35 31l22 57 7 10c3 3 33 27 39 10 10-24 5-57 6-81 1-7-2-18 4-20l3-4c42-11 52 40 63 67 10 23 31 26 45 5l1-6" />
</circle>

Note, if your element already has cx or cy attributes set, you will want to remove them, as the path here takes over positioning.

OK, but look now, we load the page, and that sucker is off to the races, not waiting for our click before it starts its journey:

How do we solve that?

Starting the animateMotion

Turns out there is a way to both stop the animateMotion running at the outset, and also a way to trigger it running when we want to. To stop the animation, we need the begin="indefinite" attribute on our element. Then to start it moving we use the beginElement() method on it from JavaScript. So now, we control the line drawing and the circle moving from the same click event. Note, that circle is in the wrong position before the animation starts. We will fix that in a moment…

Fixing the circle loading in the wrong position

Remember a minute ago when I told you to remove the cx and cy on the circle? Well, I lied. Kind of. We need them there at the start, but then we need to remove them when the animation and path take over positioning. So I’ll add them back in the SVG and then amend the JS to remove those attributes when the animation starts. If you needed to add them back, you could use the ever handy finished.then() part of WAAPI to set them back when the line animation finishes. You can also control the fill of the animateMotion with the freeze value to keep the last animation state, or remove if you want it to reset back as if it had never occurred (the equivalents of backwards and forwards in CSS animation fill).

At this point, our SVG solution is complete.

CSS Motion path

What about if we want a ‘normal’ element to follow that path? We can use CSS motion-path.

We need the SVG and path to share a common context as we will be relying on absolute positioning. So we wrap the SVG in an element, then add our circle as a sibling element to the SVG.

This way is a little more fraught because you need to position your standard element to begin with, and it can be a little bit ‘magic-numbery’. But, we set that same path value like this in the CSS:

.circle {
  height: 10px;
  width: 10px;
  position: absolute;
  top: 12.5px;
  left: 12.5px;
  background-color: #f90;
  border-radius: 50%;
  display: block;
  offset-path: path(
    "M7 53 2 23C1 11 10-1 22 0c14 1 29 20 35 31l22 57 7 10c3 3 33 27 39 10 10-24 5-57 6-81 1-7-2-18 4-20l3-4c42-11 52 40 63 67 10 23 31 26 45 5l1-6"
  );
}

And then, using WAAPI we can animate it along that path when needed:

circle.animate([{ offsetDistance: "0%" }, { offsetDistance: "100%" }], timing);

And here is the finished version using another DOM element:

Summary

I’ve never needed to do this kind of thing before but it’s nice to know there are Web APIs to make it all possible without needing a library for something this simple. Obviously, if you are doing more involved animations something like Greensock would make more sense.

Here’s some reference information for further reading:

If this post was useful, Say thanks with a coffee. Every donation helps keep me going! 🙏


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK