Easy Bezier Curves on the HTML5 Canvas
This is a follow up post to the vector.js post that I made last week, and depends on the vector class I released then. Please take a look at that post first.
Last week, I released a simple Ecmascript 6 vector class that I wrote. It's mildly interesting on its own, but this post is the real reason I wrote that other one. Using that vector class, I then went ahead and wrote my own bezier curve class, that supports an arbitrary number of control points. Before I continue, here's the code:
I was surprised by how easy the bezier curve algorithm was to implement. Basically, you loop over all your control points, finding the point that lies in a specific percentage of the distance between the current point and the next one. You then repeat this process until you have just a single point remaining. This results in a smooth curve that is skewed towards all of the given control points.
In my implementation, I did it recursively, with all the magic happening in the interpolate() function. It performs the above algorithm given a time between 0 and 1, and spits out the interpolated value. I called it a time here because bezier curves are often used to smooth out animations, and I travel along the line in the curve() function when applying it to the given canvas rendering context.
To use it, you first create an instance like so:
var bz = new BezierCurve([
new Vector(38, 41),
new Vector(96, 302),
new Vector(807, 12)
]);
Then, in your rendering function, you can ask your new bezier curve to add itself to any given canvas rendering context like this:
// ...
context.beginPath();
// ...
bz.curve(context, 32);
// ...
context.stroke();
// ...

The second argument in the curve() call above is the number of segments to use when rendering. This tells the bezier curve class how many different points along the line it should calculate. More segments make it smoother, but will consume more processing power when first calculated. Pick just enough that you can't tell that the bezier curve is made up of lines - this is different for every curve.
I added a caching function to it too, so if you call the curve() function with the same number of segments more than once, it uses the interpolated values it calculated previously instead of calculating them all over again. This isn't the case if you call it with a different number of segments each time, however. If you want to do this, I suggest that you create a new instance for each different segment number that you need.
That concludes this post on my own bezier curve class. It's a little bit buggy in places (see the gif above), but I haven't managed to track down the issue. It should work fine for most purposes, though. If you do manage to spot the mistake, please let me know!
Update 24th January 2016: I've replaced the original code with an embedded version of the gist in order to keep it up to date, since I've revised it slightly sinec writing this blog post.