Animate Anything Along an SVG Path
Publikováno: 19.1.2022
Learn how to code creative animations using SVG paths and the getPointAtLength() function.
The post Animate Anything Along an SVG Path appeared first on Codrops.
SVG is a very neat format to display any illustration, icon or logo on a website. Furthermore, they can be animated in CSS or JavaScript to make them more attractive. But SVG can also be used for their data only, without the visual! Let me explain…
An SVG is a vector image format, which means it is not made of coloured pixels, but math functions that, once interpreted, can be rendered on screen. Since the browser must convert the file from functions to actual pixels, it also let us access a wide variety of methods to either manipulate, or retrieve data from the math.
In today’s article, we will explore the function getPointAtLength() and see how we can use the data of an SVG path for creative use cases such as the demo below.
See the Pen Random dots along path – CodePen Challenge by Louis Hoebregts (@Mamboleoo) on CodePen.
If you are not familiar with SVG, this CSS-Tricks article is a good start!
The method getPointAtLength()
If we take a look at the MDN documentation page about the method it says:
The SVGGeometryElement.getPointAtLength()
method returns the point at a given distance along the path.
The method will give us the coordinates of a point that is precisely along the path at a specific distance that we send as a parameter.
For example path.getPointAtLength(10)
will return an SVGPoint (an object) with x & y coordinates.
Since we need to give the distance of our point, it means we will most likely need to know how long is our path. Luckily, the SVG API has a method getTotalLength() available to any SVGGeometryElement
that returns the total length of our element!
The SVGGeometryElement
variable refers to all SVG tags that are created from a geometry (path, rect, circle,…) so this does not include image, filter, clip-path,… tags.
Let’s see how we can then use this function to animate a circle along a given path using the GreenSock library.
To do so, we need a JavaScript object that will contain the animated values (as gsap cannot animate number variables directly) and set a property distance
to zero.
We then create a tween that will update the distance
value from 0 to the total length of our path.
Finally on each frame, we retrieve a point along the path based on the animated distance value, and we update the cx
and cy
attributes of our circle to make it move
// Create an object that gsap can animate
const val = { distance: 0 };
// Create a tween
gsap.to(val, {
// Animate from distance 0 to the total distance
distance: path.getTotalLength(),
// Function call on each frame of the animation
onUpdate: () => {
// Query a point at the new distance value
const point = path.getPointAtLength(val.distance);
// Update the circle coordinates
circle.setAttribute('cx', point.x);
circle.setAttribute('cy', point.y);
}
});
See the Pen Animate single element along path by Louis Hoebregts (@Mamboleoo) on CodePen.
If the effect you want to achieve is just animating one element along an SVG path such as in the demo above, you could check the MotionPathPlugin by GreenSock. It will let you animate easily any DOM element from a path you provide. (plus it’s free!)
Using the points coordinates for particles
I love particles, it’s no breaking news. Which is why, when I learn a new technique I always try to implement something with them!
Let’s see how instead of a single circle moving along a path, we could make many more circles exploding like a bomb fuse
The overall logic of this animation is exactly the same as before, except that on each frame we will create a new circle element and animate it. As you can see, the setup is very similar.
const svg = document.querySelector('svg');
const fuse = svg.querySelector('.fuse');
const val = { distance: 0 };
gsap.to(val, {
distance: fuse.getTotalLength(),
repeat: -1,
duration: 5,
onUpdate: () => {
// Query a point at the new distance value
const point = fuse.getPointAtLength(val.distance);
// Create a new particle
createParticle(point);
}
});
The createParticle
function will be called on each frame to make a new particle pop and fade out. Here are the steps of the animation:
- Create a new circle element and append it to the SVG
- Set the coordinates from the point we calculated with
getPointAtLength
- Define a random radius and color for each
- Animate that particle
cx
&cy
attributes to a random position - Once the animation is complete, remove the particle from the DOM
function createParticle (point) {
// Create a new circle element
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
// Prepend the element to the SVG
svg.prepend(circle);
// Set the coordinates of that circle
circle.setAttribute('cx', point.x);
circle.setAttribute('cy', point.y);
// Define a random radius for each circle
circle.setAttribute('r', (Math.random() * 2) + 0.2);
// Define a random color
circle.setAttribute('fill', gsap.utils.random(['#ff0000', '#ff5a00', '#ff9a00', '#ffce00', '#ffe808']));
// Animate the circle
gsap.to(circle, {
// Random cx based on its current position
cx: '+=random(-20,20)',
// Random cy based on its current position
cy: '+=random(-20,20)',
// Fade out
opacity: 0,
// Random duration for each circle
duration: 'random(1, 2)',
// Prevent gsap from rounding the cx & cy values
autoRound: false,
// Once the animation is complete
onComplete: () => {
// Remove the SVG element from its parent
svg.removeChild(circle);
}
});
}
See the Pen Bomb fuse particles by Louis Hoebregts (@Mamboleoo) on CodePen.
To make the animation prettier I’m also animating the stroke-dashoffset
of the fuse to make it more realistic. You can check this article for more details on such animation.
Using the coordinates in WebGL
So far we have only animated SVG elements next to the path. But sometimes all we need are the raw coordinates from the path, but not the path itself. For example, if we want to animate particles in a 2D Canvas or in WebGL like in the animation below.
See the Pen Make it POP! by Louis Hoebregts (@Mamboleoo) on CodePen.
The following chapter expects you to know how to setup and run a three.js scene.
Here are the key concepts of this animation:
- Get the path and its total length
- Loop along the path until you reach its length
- Get the point that exists on the index distance
- On each iteration, create a Vector3 at the point’s coordinates
- Push the vector into an array of vertices
- Create a geometry from the vertices
- Create a Points mesh and add it into your scene
// Get the Path in the DOM
const path = document.querySelector("path");
// Store the total length of the path
const length = path.getTotalLength();
// Empty array to store all vertices
const vertices = [];
// Loop along the path
for (let i = 0; i < length; i += 0.2) {
// Get the coordinates of a point based on the index value
const point = path.getPointAtLength(i);
// Create a new vector at the coordinates
const vector = new THREE.Vector3(point.x, -point.y, 0);
// Randomize a little bit the point to make the heart fluffier
vector.x += (Math.random() - 0.5) * 30;
vector.y += (Math.random() - 0.5) * 30;
vector.z += (Math.random() - 0.5) * 70;
// Push the vector into the array
vertices.push(vector);
}
// Create a new geometry from the vertices
const geometry = new THREE.BufferGeometry().setFromPoints(vertices);
// Define a pink material
const material = new THREE.PointsMaterial( { color: 0xee5282, blending: THREE.AdditiveBlending, size: 3 } );
// Create a Points mesh based on the geometry and material
const particles = new THREE.Points(geometry, material);
// Offset the particles in the scene based on the viewbox values
particles.position.x -= 600 / 2;
particles.position.y += 552 / 2;
// Add the particles in to the scene
scene.add(particles);
See the Pen Untitled by Louis Hoebregts (@Mamboleoo) on CodePen.
Creating a heart of particles is fun, but animating those particles is even FUNNIER
First we setup a global timeline so that all tweens will be grouped together and we’ll be able to repeat all of them once the last animation is complete.
// Create a global gsap timeline that contains all tweens
const tl = gsap.timeline({
repeat: -1,
yoyo: true
});
While creating the vector, we attach a gsap tween on it so that it animates from the center of the heart. We can calculate the x & y coordinates for the start based on the SVG viewBox attributes viewBox="0 0 600 552"
.
Since y
axis are in the other direction in SVG, we apply a negative value. (y
is going up in WebGL, while in CSS & SVG it’s going down).
Each vector’s animation will have a delay that is calculated from its own distance along the path so that will create a nice flow of particles going round.
for (let i = 0; i < length; i += 0.1) {
[...]
// Create a tween for that vector
tl.from(vector, {
x: 600 / 2, // Center X of the heart
y: -552 / 2, // Center Y of the heart
z: 0, // Center of the scene
ease: "power2.inOut",
duration: "random(2, 5)" // Random duration
},
i * 0.002 // Delay calculated from the distance along the path
);
}
Finally, we need to update the geometry of the Points mesh on each frame as the Vector3 objects are being animated but the geometry is not aware of that.
function render() {
requestAnimationFrame(render);
// Update the geometry from the animated vertices
geometry.setFromPoints(vertices);
}
And voilà
See the Pen Create word from SVG Path – WebGL by Louis Hoebregts (@Mamboleoo) on CodePen.
What’s next?
You tell me! Now that you can extract the coordinates of points along an SVG path, try to apply those data anywhere else
What about animating lines instead of particles? Or create a new path based on random points that you pick along another one?
Explore this technique and share your results with me on Twitter, I can’t wait to see what you’ll come up with
See you another time,
Mamboleoo
The post Animate Anything Along an SVG Path appeared first on Codrops.