Placing some offset directional arrows along a path, take 3.
This method is total overkill. At each desired arrow point along the path, it calculates points along the path every 2 pixels and its corresponding offset point for an arrow, until the arrow is roughly the desired length, and then uses an interpolator to smooth it out. This ensures that arrows in tight curves and straight arrows still end up roughly the same length even though the points offset from the path end up closer together.
marker-segment and marker-pattern should make this easier eventually.
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
path {
fill: none;
stroke: #fc0;
stroke-width: 2px;
}
.arrow {
stroke-width: 1px;
stroke: #444;
}
#arrowhead path {
stroke: none;
fill: black;
}
</style>
</head>
<body>
<svg version="1.1" xmlns="https://www.w3.org/2000/svg" width="960" height="500">
<path id="loop" d="M636.5,315c-0.4-18.7,1.9-27.9-5.3-35.9
c-22.7-25-107.3-2.8-118.3,35.9c-7,24.4,20.6,37.2,16,71c-4,29.6-30.8,60.7-56.5,61.1c-30.8,0.4-32.9-43.8-81.7-70.2
c-50.9-27.6-110.1-12.9-125.2-9.2c-66.1,16.4-82.2,56.9-109.2,47.3c-38-13.6-55.9-112.1-19.8-143.5c39-34,121.2,27.7,148.1-3.8
c18-21.1,3.1-74.3-25.2-105.3c-31.1-34.1-70.1-32.4-105.3-76.3c-8.2-10.2-16.9-23.8-15.3-39.7c1.2-11.4,7.5-23.3,15.3-29
c33.8-25,101.6,62.6,193.1,59.5c40.1-1.3,38.7-18.5,99.2-38.9c126.2-42.6,242.4-4.9,297.7,13c54.7,17.7,105.4,35,129.8,82.4
c13,25.3,22.9,67.7,4.6,87c-11.6,12.3-25.1,5.1-46.6,20.6c-2.8,2-28.9,21.4-32.1,49.6c-3.1,27.4,18.7,35,29,70.2
c8.8,30.1,8.5,77.8-18.3,99.2c-32.3,25.8-87,0.6-100-5.3c-69.6-32-67.2-88.4-73.3-109.2z"/>
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="1" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<path id="test" />
</defs>
</svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var path = document.getElementById("loop"),
test = document.getElementById("test"),
length = path.getTotalLength(),
desiredArrowLength = 40,
offset = 10,
numArrows = 20;
var line = d3.line()
.curve(d3.curveCardinal.tension(0.5));
d3.select("svg").selectAll(".arrow")
.data(d3.range(numArrows))
.enter()
.append("path")
.attr("class", "arrow")
.attr("marker-end", "url(#arrowhead)")
.attr("d", curve);
function curve(d) {
var l = length * d / numArrows,
points = [],
i = 0;
test.setAttribute("d", "");
// Could get even more exact by varying the step...
while (test.getTotalLength() < desiredArrowLength) {
points.push(offsetPoint(l + i * 2));
test.setAttribute("d", line(points) + "L" + offsetPoint(l + i * 2 + 0.1))
i++;
}
return test.getAttribute("d");
}
function offsetPoint(l) {
var angle = angleAtLength(l) - Math.PI / 2,
point = pointAtLength(l);
return [
point[0] + offset * Math.cos(angle),
point[1] + offset * Math.sin(angle)
];
}
function pointAtLength(l) {
var xy = path.getPointAtLength(l);
return [xy.x, xy.y];
}
// Approximate tangent
function angleAtLength(l) {
var a = pointAtLength(Math.max(l - 0.01,0)), // this could be slightly negative
b = pointAtLength(l + 0.01); // browsers cap at total length
return Math.atan2(b[1] - a[1], b[0] - a[0]);
}
</script>
https://d3js.org/d3.v4.min.js