JSFiddle - React, Tailwind, and code Playground
JavaScript
const cnv = document.querySelector('canvas');
cnv.width = 600;
cnv.height = 600;
const ctx = cnv.getContext('2d');
drawSpline(ctx, createPoints(100, 100, 70, 4, 40), 0.5);
function createPoints(cx, cy, r, n, d) {
const points = [];
for (let i = 0; i < n; i++)
points.push(
r * Math.cos(Math.PI * 2 / n * i) + Math.round(Math.random() * d - d / 2) + cx,
r * Math.sin(Math.PI * 2 / n * i) + Math.round(Math.random() * d - d / 2) + cy
);
return points;
}
function drawSpline(ctx, pts, t) {
ctx.lineWidth = 4;
ctx.save();
var cp = []; // array of control points, as x0,y0,x1,y1,...
var n = pts.length;
// Append and prepend knots and control points to close the curve
pts.push(pts[0], pts[1], pts[2], pts[3]);
pts.unshift(pts[n - 1]);
pts.unshift(pts[n - 1]);
for (var i = 0; i < n; i += 2) {
cp = cp.concat(getControlPoints(pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5], t));
}
cp = cp.concat(cp[0], cp[1]);
ctx.beginPath();
ctx.moveTo(pts[2], pts[3]);
for (var i = 2; i < n + 2; i += 2) {
ctx.bezierCurveTo(cp[2 * i - 2], cp[2 * i - 1], cp[2 * i], cp[2 * i + 1], pts[i + 2], pts[i + 3]);
}
ctx.closePath();
ctx.fill()
ctx.restore();
}
function getControlPoints(x0, y0, x1, y1, x2, y2, t) {
// x0,y0,x1,y1 are the coordinates of the end (knot) pts of this segment
// x2,y2 is the next knot -- not connected here but needed to calculate p2
// p1 is the control point calculated here, from x1 back toward x0.
// p2 is the next control point, calculated here and returned to become the
// next segment's p1.
// t is the 'tension' which controls how far the control points spread.
// Scaling factors: distances from this knot to the previous and following knots.
var d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2));
var d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
var fa = t * d01 / (d01 + d12);
var fb = t - fa;
var p1x = x1 + fa *...