I'm trying to create directional arrows on an svg path that's being rendered using d3. I obtain the start points using the path string and the svg-path-properties npm package. I then push these points to an array, and in another function, I map these points onto the original svg path with the d3 logic to create triangles at each point.
public mapArrowPoints(): void {
this.arrowPoints.forEach((point: ArrowPointObject) => {
const path = d3.path();
path.moveTo(parseInt(point.x, 10), parseInt(point.y, 10));
path.lineTo(parseInt(point.x, 10) 8, parseInt(point.y, 10) 2);
path.lineTo(parseInt(point.x, 10) 0, parseInt(point.y, 10) 8);
path.closePath();
const svg = d3.select('svg#topLevel');
const defs = svg.insert('defs', '#curve');
const marker = defs.append('marker');
marker
.attr('id', 'triangle')
.attr('viewBox', '0 -5 10 10')
.attr('markerWidth', 4)
.attr('markerHeight', 4)
.attr('orient', 'auto')
.append('path')
.attr('d', path.toString())
.attr('fill', 'red')
.attr('stroke', 'black')
.attr('stroke-width', '3px');
svg
.select('#curve')
.attr('marker-start', 'url(#triangle)')
.attr('marker-mid', 'url(#triangle)')
.attr('marker-end', 'url(#triangle)');
});
}
Now, when I check the browser, I'm able to see my main path, but no triangles. I can inspect the DOM, and see the svg
, the defs
, marker
, and the path
they're supposed to be appended onto. I can also select the marker path itself, and see where it should be rendering (a blue box shows where the triangle should be). I can't quite figure out why the triangles aren't showing. Any help will be much appreciated. I've created a stackblitz example for experimenting with solutions.
Note:
I can make the triangles appear on the path without the additional defs
and marker
logic as shown here:
svg
.append("path")
.attr("d", path.toString())
.attr("stroke", "black")
.attr("stroke-width", "3px");
This shows where the triangles should be. End of the day, the mission here is to cause these triangles to utilize the orient: "auto"
functionality that's available to the marker attribute. If anybody knows a way to orient these triangles without using the markers, I'm open to that possibility. After all, I'm able to render these triangles without the marker
tag, so maybe I don't need it? I've included a commented out section in the stackblitz for experimenting with this method.
CodePudding user response:
Besides the fact that you have lots of marker elements with the same id, your marker has a viewBox that makes the triangle to fall outside the marker box. A proper viewBox for the path you have with a 3units stroke would be bigger than it's bounding box so I would use 'viewBox="18 5 14 14"'. You may want to choose a different viewBox.
Here you can see the path you want to use as a marker:
svg{border: solid}
<svg viewBox="18 5 14 14" width="300" >
<path d="M20,7L28,9L20,15Z" fill="red" stroke="black" stroke-width="3"></path>
</svg>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Once you have this wiewBox for the marker you will see the marker but it falls outside the path. This is because the marker ha a refX and a refY attributes refferencing the point where the marker attaches itself to the path. By default this point is {0,0}. I'l be using refX="18.5" refY="9". I'm choosing 9 becose the tip of the triangle has y=9 (d="M20,7L28,9L20,15Z")
In the next example I'm marking this point with a golden circle. You may want to choose a different one.
svg{border: solid}
<svg viewBox="18 5 14 14" width="300" >
<path d="M20,7L28,9L20,15Z" fill="red" stroke="black" stroke-width="3"></path>
<circle cx="18.5" cy="9" r=".5" fill="gold"/>
</svg>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
And this is how the path with the marker would look like:
<svg viewBox="0 0 700 800" >
<defs>
<marker id="triangle" viewBox="18 5 14 14" markerWidth="8" markerHeight="8" orient="auto" refX="18.5" refY="9">
<path d="M20,7L28,9L20,15Z" fill="red" stroke="black" stroke-width="3px"></path>
</marker>
</defs>
<path fill="none" stroke="black" id="curve" d="M 0.05965662,9.8756182 C 665.77394,-70.124378 654.96347,387.98451 654.96347,387.98451 L 45.773941,621.3042 650.91307,783.4508" marker-start="url(#triangle)" marker-mid="url(#triangle)" marker-end="url(#triangle)"></path>
</svg>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>