I'm trying to work out how to draw a squiggly line on an arbitrary SVG path
element. The path is generated by a React Component. For example, I'm trying to replicate the line in this question:
Is there an easy way to do this in SVG and/or JavaScript generated paths?
I've considered joining up a series of curves using the s
path command, but then I would need to calculate points along the curve. I've also considered some sort of displacement filter but I'm not really sure where to start.
Seems to me the easiest way would be step along the path. Then, at each step, insert a quadratic bezier curve with a control point that is half way between them and perpendicular to the curve. Then for the next step switch the side that the control point is on.
function makeSquiggle(squigglePathId, followPathId, squiggleStep, squiggleAmplitude)
{
var followPath = document.getElementById(followPathId);
var pathLen = followPath.getTotalLength();
// Adjust step so that there are a whole number of steps along the path
var numSteps = Math.round(pathLen / squiggleStep);
var pos = followPath.getPointAtLength(0);
var newPath = "M" + [pos.x, pos.y].join(',');
var side = -1;
for (var i=1; i<=numSteps; i++)
{
var last = pos;
var pos = followPath.getPointAtLength(i * pathLen / numSteps);
// Find a point halfway between last and pos. Then find the point that is
// perpendicular to that line segment, and is squiggleAmplitude away from
// it on the side of the line designated by 'side' (-1 or +1).
// This point will be the control point of the quadratic curve forming the
// squiggle step.
// The vector from the last point to this one
var vector = {x: (pos.x - last.x),
y: (pos.y - last.y)};
// The length of this vector
var vectorLen = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
// The point halfwasy between last point and tis one
var half = {x: (last.x + vector.x/2),
y: (last.y + vector.y/2)};
// The vector that is perpendicular to 'vector'
var perpVector = {x: -(squiggleAmplitude * vector.y / vectorLen),
y: (squiggleAmplitude * vector.x / vectorLen)};
// No calculate the control point position
var controlPoint = {x: (half.x + perpVector.x * side),
y: (half.y + perpVector.y * side)};
newPath += ("Q" + [controlPoint.x, controlPoint.y, pos.x, pos.y].join(','));
// Switch the side (for next step)
side = -side;
}
var squigglePath = document.getElementById(squigglePathId);
squigglePath.setAttribute("d", newPath);
}
makeSquiggle("squiggle", "follow", 25, 20);
#follow {
fill: none;
stroke: grey;
stroke-width: 2;
}
#squiggle {
fill: none;
stroke: red;
stroke-width: 2;
}
<svg width="500" height="400">
<path id="follow" d="M 50,300 C 100,100 300,0, 350,250 L 450,200"/>
<path id="squiggle" d="M0,0"/>
</svg>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With