在 D3 强制布局链接标记上缩放箭头
Posted
技术标签:
【中文标题】在 D3 强制布局链接标记上缩放箭头【英文标题】:Scaling an arrowhead on a D3 force layout link marker 【发布时间】:2012-06-22 16:51:19 【问题描述】:我在 d3 力有向图中有以下代码,我试图根据一个值(从 1 到 3)改变链接及其相关箭头的大小。笔画粗细会随值而变化,但箭头不会保持在正确的位置。当笔画粗细从 1 变为 3 时,它往往会从末尾移回。关于在更改笔画值时如何保持箭头(标记)正确对齐的任何想法?非常感谢!
var link = vis.selectAll("line.link")
.data(json.links)
.enter().append("svg:line")
.attr("class", "link")
.style("stroke-width", function(d) return Math.sqrt(d.value); )
.attr("x1", function(d) return d.source.x; )
.attr("y1", function(d) return d.source.y; )
.attr("x2", function(d) return d.target.x; )
.attr("y2", function(d) return d.target.y; )
.attr("marker-end", "url(#arrowGray)")
.on("click", function(d)
link.style("stroke","#dddddd");
node.style("stroke","#FFFFFF");
d3.select(this).style("stroke","red");
link.attr("marker-end", null);
link.attr("marker-end", "url(#arrowGray)");
d3.select(this).attr("marker-end", null);
d3.select(this).attr("marker-end", "url(#arrowRed)");
clickLink(d);
);
defs.append("svg:marker")
.attr("id", "arrowGray")
.attr("viewBox","0 0 10 10")
.attr("refX","20")
.attr("refY","5")
.attr("markerUnits","strokeWidth")
.attr("markerWidth","9")
.attr("markerHeight","5")
.attr("orient","auto")
.append("svg:path")
.attr("d","M 0 0 L 10 5 L 0 10 z")
.attr("fill", "#BBBBBB");
【问题讨论】:
这是缩放标记的正确方法,所以如果这对您不起作用,您可能必须为不同的尺寸定义不同的标记。通过不设置refX
/refY
而是相应地设置d
属性,您也可能获得更大的成功。您能否给我们一个发生此问题的完整工作示例?
【参考方案1】:
问题
这个jsFiddle 演示了有问题的问题。标记 def 的值与它所连接的元素的笔画宽度有关(在这种情况下为链接线)。请参阅markerUnits spec。通过将strokeWidth
用于markerUnits
属性,不同大小箭头的坐标将略有不同。简而言之,一种尺寸箭头的适当值不会正确转换为其他尺寸。
解决方案 1:多个标记
如 cmets 中所述,一种解决方案是为每个需要的 strokeWidth
创建一个不同的标记。这只有在您事先知道需要什么尺寸时才有效。
解决方案 2:修改线条
另一种选择是修改线条的终点。不要让线在节点的中心终止,而是在节点的外边缘终止。这个jsFiddle 证明了这一点。这减少了移动箭头的需要,因为我们现在可以在行尾绘制它。
此解决方案涉及一些数学运算,以确定 line
的 x2
和 y2
值应该是什么。因此,它可能不适合具有大量边的系统。
var nodeRadius = 10;
var lineX2 = function (d)
var length = Math.sqrt(Math.pow(d.target.y - d.source.y, 2) + Math.pow(d.target.x - d.source.x, 2));
var scale = (length - nodeRadius) / length;
var offset = (d.target.x - d.source.x) - (d.target.x - d.source.x) * scale;
return d.target.x - offset;
;
var lineY2 = function (d)
var length = Math.sqrt(Math.pow(d.target.y - d.source.y, 2) + Math.pow(d.target.x - d.source.x, 2));
var scale = (length - nodeRadius) / length;
var offset = (d.target.y - d.source.y) - (d.target.y - d.source.y) * scale;
return d.target.y - offset;
;
var link = svg.selectAll("line.link")
.data(graph.links)
.enter().append("svg:line")
.attr("class", "link")
.style("stroke-width", function (d)
return Math.sqrt(d.value);
)
.attr("x1", function (d)
return d.source.x;
)
.attr("y1", function (d)
return d.source.y;
)
.attr("x2", lineX2)
.attr("y2", lineY2)
.attr("marker-end", "url(#arrowGray)")
.on("click", function (d)
link.style("stroke", "#dddddd");
node.style("stroke", "#FFFFFF");
d3.select(this).style("stroke", "red");
link.attr("marker-end", null);
link.attr("marker-end", "url(#arrowGray)");
d3.select(this).attr("marker-end", null);
d3.select(this).attr("marker-end", "url(#arrowRed)");
);
var defs = svg.append('defs');
defs.append("svg:marker")
.attr("id", "arrowGray")
.attr("viewBox", "0 0 10 10")
.attr("refX", "10")
.attr("refY", "5")
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", "10")
.attr("markerHeight", "5")
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", "#000");
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", nodeRadius)
.style("fill", function (d)
return color(d.group);
)
.call(force.drag);
node.append("title")
.text(function (d)
return d.name;
);
force.on("tick", function ()
link.attr("x1", function (d)
return d.source.x;
)
.attr("y1", function (d)
return d.source.y;
)
.attr("x2", lineX2)
.attr("y2", lineY2)
node.attr("cx", function (d)
return d.x;
)
.attr("cy", function (d)
return d.y;
);
);
【讨论】:
【参考方案2】:您的refX
可能太大了。尝试将其设置为 1 左右。
【讨论】:
这没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。 不,@azurefrog,这是问题的答案。以上是关于在 D3 强制布局链接标记上缩放箭头的主要内容,如果未能解决你的问题,请参考以下文章