Вы можете создать контрольные точки для точки на основе точек до и после:
function cp(a, b, c) {
if (!a || !c) return b;
return {
x: b.x + (c.x - a.x) * .25,
y: b.y + (c.y - a.y) * .25
};
}
и соответствующих функций Безье
function bezier4(t,a,b,c,d) {
var u = 1-t, fa = u*u*u, fb = 3*u*u*t, fc = 3*u*t*t, fd = t*t*t;
return {
x: a.x*fa + b.x*fb + c.x*fc + d.x*fd
y: a.y*fa + b.y*fb + c.y*fc + d.y*fd
};
}
function bezier(t, ...points) {
var last = points.length-1;
t *= last;
if(t <= 0) return points[0];
if(t >= last) return points[last];
var i = Math.floor(t);
if(t === i) return points[i];
return bezier4(
t-i,
points[i],
cp(points[i-1], points[i], points[i+1]),
cp(points[i+2], points[i+1], points[i]),
points[i+1]
);
}
и небольшой фрагмент, принимающий точкии создание SVG.
var points = [{x:0, y:0}, {x:-30, y:-50}, {x:-10, y:-100}, {x:-30, y:-150}];
function cp(a, b, c) {
if (!a || !c) return b;
return {
x: b.x + (c.x - a.x) * .25,
y: b.y + (c.y - a.y) * .25
};
}
var pointsWithControlPoints = points.flatMap((pt, i) => [
cp(points[i + 1], pt, points[i - 1]),
pt,
cp(points[i - 1], pt, points[i + 1]),
])
.slice(1, -1) // remove the control points before the first point and after the last one
.map(pt => [pt.x, pt.y]);
var bounds = points.reduce((bounds, pt) => {
bounds.top = Math.min(bounds.top, pt.y);
bounds.left = Math.min(bounds.left, pt.x);
bounds.bottom = Math.max(bounds.bottom, pt.y);
bounds.right = Math.max(bounds.right, pt.x);
return bounds;
}, {
top: points[0].x,
left: points[0].y,
bottom: points[0].x,
right: points[0].y
});
bounds.width = bounds.right - bounds.left;
bounds.height = bounds.bottom - bounds.top;
document.body.innerHTML = `<svg
viewBox="${bounds.left-10} ${bounds.top-10} ${bounds.width+20} ${bounds.height+20}"
width="${bounds.width+20}"
height="${bounds.height+20}"
>
<path fill="none"
stroke="black"
stroke-dasharray="4"
d="M${pointsWithControlPoints[0]}C${pointsWithControlPoints.slice(1)}"
/>
${points.map(pt => `<rect x="${pt.x - 4}" y="${pt.y - 4}" width="8" height="8" />`).join("\n")}
</svg>`;