I have recently written about SVG Animation with Anime.js and SVG Bezier Curves in JavaScript. In this article I'll bring the two together with a project to animate SVG Bezier curves.
Project Overview
This project consists of an HTML file, a JavaScript file with a class for animating SVG Bezier curves and a JavaScript file utilising the class. You can download the files as a ZIP or from the GitHub repository. Both also include the minimized anime.js file.
Source Code Links
The HTML
I won't show the entire HTML file as it's very boring but does include this SVG element.
<svg width="800" height="600" id="svg"> <rect width="800" height="600" style="fill:rgb(0,0,0)" /> </svg>
The AnimatedSVGBezier Class
This is the animatedsvgbezierlibrary.js file containing the AnimatedSVGBezier class.
animatedsvgbezierlibrary.js
class AnimatedSVGBezier { static #generatePath(points, relative) { let type = null; if(points.length === 3) { type = "Q"; } else if(points.length === 4) { type = "C"; } else if(points.length % 2 === 0) { type = "C"; } else { throw 'Number of points must be 3 or an even number more than 3'; } const pathPoints = ["M ", points[0][0], ",", points[0][1], type]; for(let p = 1, l = points.length; p < l; p++) { if(p >= 4 && p % 2 === 0) pathPoints.push("S"); pathPoints.push(points[p][0]); pathPoints.push(","); pathPoints.push(points[p][1]); } return pathPoints.join(" "); } static animateBezier(settings) { const startPoints = []; // clone points for(let point of settings.points) { startPoints.push([point[0],point[1]]); } for(let point of startPoints) { switch(settings.startType) { case "top": point[1] = 0; break; case "bottom": point[1] = settings.svgSize.height; break; case "left": point[0] = 0; break; case "right": point[0] = settings.svgSize.width; break; } } const startPath = AnimatedSVGBezier.#generatePath(startPoints); const finalPath = AnimatedSVGBezier.#generatePath(settings.points); const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttributeNS(null, "d", startPath); path.setAttributeNS(null, "stroke", settings.strokeColor); path.setAttributeNS(null, "fill", "transparent"); path.setAttributeNS(null, "stroke-width", settings.strokeWidth); document.getElementById(settings.id).appendChild(path); anime( { targets: path, d: finalPath, duration: settings.duration, easing: 'easeInOutQuad' }); } }
#generatePath
This is a private function (denoted by the # prefix) which is copied from the earlier SVG Bezier Curves in JavaScript project. Basically it takes a set of points and generates an SVG element from them.
animateBezier
This is a public static method which gets all the information it needs as a settings object and uses it to animate a Bezier curve using the anime.js library.
We need two sets of points, the starting points and the final points. I have therefore cloned the points array from settings and then shifted one of each pair of coordinates to the top, bottom, left or right depending on the startType.
This may be a bit confusing so as an example, if the startType is top the y coordinates are all set to 0, the top line, but the x coordinates are left as they are. We therefore start animating the curve from a straight line across the top, from where it will move down to its ultimate position.
We then call AnimatedSVGBezier.#generatePath on both sets of points and create a curve with the startPath.
Finally we use the anime.js library method anime to animate the curve out to its final position.
Using the AnimatedSVGBezier Class
We can now put the class to use in a separate file, animatedsvgbezier.js.
animatedsvgbezier.js
"use strict" window.onload = function() { bezier(); } function randomPosition(max) { return Math.floor(Math.random() * max); } function generateRandomPoints(xmax, ymax) { const pointcounts = [3,4,6,8,10,12]; const pointcount = pointcounts[Math.floor(Math.random() * 6)]; let points = []; for(let i = 0; i < pointcount; i++) { const point = [randomPosition(xmax), randomPosition(ymax)]; points.push(point); } return points; } function getRandomColour() { const r = Math.floor(Math.random() * 255); const g = Math.floor(Math.random() * 255); const b = Math.floor(Math.random() * 255); return `rgb(${r},${g},${b})`; } function getRandomStartType() { const startTypes = ["top","left","bottom","right"]; return startTypes[Math.floor(Math.random() * 4)]; } function bezier() { const beziers = []; for(let i = 0; i <= 256; i++) { const bezier = { id: "svg", svgSize: {width: 800, height: 600}, points: generateRandomPoints(800, 600), startType: getRandomStartType(), strokeColor: getRandomColour(), strokeWidth: 1, duration: Math.floor(Math.random() * 8000) + 2000 } beziers.push(bezier); } for(let bezier of beziers) { AnimatedSVGBezier.animateBezier(bezier); } }
window.onload
This function simply calls the bezier function which I'll look at in a moment.
randomPosition, generateRandomPoints, getRandomColour and getRandomStartType
The curves are all created with random properties so we have a set of four functions to generate suitable random values.
The bezier Function
This function creates an array of objects representing Bezier curves which can be passed to AnimatedSVGBezier.animateBezier as the settings argument. As you can see these are mostly set to random values generated by the functions above.
Finally we just iterate the array, passing each element to AnimatedSVGBezier.animateBezier. The anime method is asynchronous so all the curves are created and animated at the same time.
Trying it Out
That's the code finished so open animatesvgbezier.html in your browser. At the moment the loop creates a whopping 256 Bezier curves but you might like to try other values in pursuit of aesthetic excellence.
Here are a few examples.
The examples I have shown here are really no more than frivolous pieces of abstract expressionist art but I hope you are able to put the AnimatedSVGBezier class to more serious use.