I am using a d3.js force-directed-graph. It has nodes and links connecting them. To create an arrow head, I use the svg and d3 combined like so:
gA.svg.append('defs').selectAll('marker').data(['arrow-head']) // binding 'arrow-head' is our way of attaching a name!
.enter().append('marker')
.classed('arrow-head', true)
.attr({
id: String,
viewBox: '0 -5 10 10',
refX: 24, // this controls the distance of the arrowhead from the end of the line segment!
refY: 0,
markerWidth: 5,
markerHeight: 5,
orient: 'auto',
})
.append('path')
.attr('d', 'M0,-5L10,0L0,5')
To attach that arrowhead to the end of each edge/link, I do:
let link = gA.svg.selectAll('.link').data(gA.force.links(), link => gA.node_id(link.source) + '--->' + gA.node_id(link.target)) // links before nodes so that lines in SVG appear *under* nodes
link.enter().append('line')
.classed('link', true)
.attr('marker-end', 'url(#arrow-head)') // add in the marker-end defined above
link.exit().remove()
What is the best way to make the distance of the arrowhead from the end of the line segment variable? The purpose of this is to have the arrowhead positioned at the edge of each node circle, as opposed to in the middle of the node. It would be preferable not to make a separate SVG def for each possible distance :)
I was thinking something like:
.attr('marker-end', var_arrow_head)
function var_arrow_head(link){
radius = link.target.radius
refX = 2 + 5*radius
magically get an altered version of arrow-head with the new refX value
return 'url(#magic-arrow-head)'
}