Javascript D3.js力布局-边缘标签放置/旋转

Javascript D3.js力布局-边缘标签放置/旋转,javascript,d3.js,label,force-layout,Javascript,D3.js,Label,Force Layout,我是D3.js的新手,我一直在玩force布局。我尝试的一件事是在链接上贴标签 一种方法是添加svg:text并手动计算translate和rotate,这可以很好地处理直线。但是,如果链接是一个svg:path(例如arc),那么它就不能按预期工作。在这些情况下,svg:textPath是建议的解决方案 在中,您可以看到通过svg:textPath向链接添加标签的简单实现。它唯一的问题是,当源定位到目标的右侧时,文本以相反的方向呈现(从我们的角度来看,从路径的角度来看,它仍然是正确的)。我的问

我是D3.js的新手,我一直在玩force布局。我尝试的一件事是在链接上贴标签

一种方法是添加
svg:text
并手动计算
translate
rotate
,这可以很好地处理直线。但是,如果链接是一个
svg:path
(例如arc),那么它就不能按预期工作。在这些情况下,
svg:textPath
是建议的解决方案

在中,您可以看到通过
svg:textPath
向链接添加标签的简单实现。它唯一的问题是,当源定位到目标的右侧时,文本以相反的方向呈现(从我们的角度来看,从路径的角度来看,它仍然是正确的)。我的问题是,如何应对

我提出的唯一“解决方案”是,在上述情况下手动交换源和目标,你可以看到它几乎起作用了


在交换发生时的状态中,您还可以看到弧翻转到另一侧,这看起来不正确(

@LarsKotthoff认为
textPath
必须遵循路径的方向是正确的。在这种情况下,路径的方向不仅定义了弧的方向,还定义了末端箭头标记的附件-这使得动态交换方向变得很困难,因为您也必须移动标记

更简单的解决方案(如果你有大量的链接,可能不是最好的)是用一个只用于文本的不可见路径来“隐藏”真正的链接路径:

var link = svg.append("svg:g").selectAll("g.link")
    .data(force.links())
  .enter().append('g')
    .attr('class', 'link');

var linkPath = link.append("svg:path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });

var textPath = link.append("svg:path")
    .attr("id", function(d) { return d.source.index + "_" + d.target.index; })
    .attr("class", "textpath");
现在,您有了一个可以正确操作的单独路径。正如您所注意到的,有两个问题-您必须更改路径方向,并且必须更改圆弧方向。看起来您可以在路径命令字符串中通过交换
扫描标志
值(),因此,您没有使用
Arx,ry 0,1
而是使用
Arx,ry 0,0
。您可以使用一个函数来创建路径字符串,从而减少一些代码重复:

function arcPath(leftHand, d) {
    var start = leftHand ? d.source : d.target,
        end = leftHand ? d.target : d.source,
        dx = end.x - start.x,
        dy = end.y - start.y,
        dr = Math.sqrt(dx * dx + dy * dy),
        sweep = leftHand ? 0 : 1;
    return "M" + start.x + "," + start.y + "A" + dr + "," + dr +
        " 0 0," + sweep + " " + end.x + "," + end.y;
}
然后可以分别更新链接路径和文本路径:

linkPath.attr("d", function(d) {
    return arcPath(false, d);
});

textPath.attr("d", function(d) {
    return arcPath(d.source.x < d.target.x, d);
});
linkPath.attr(“d”,函数(d){
返回弧路径(假,d);
});
textPath.attr(“d”,函数(d){
返回弧径(d.source.x

请参阅工作代码:

您不能为
textPath
指定文本方向--文本始终遵循路径的方向。因此,您拥有的解决方案是唯一可行的解决方案。不建议在数据联接中为每个数据元素创建多个节点。如果/当您更新数据并执行以下操作时,将导致意外行为第二次加入。这里有一些关于原因的详细信息:要点已被采纳,但如果您不打算在以后动态更新数据,那么这样做是完全可以接受的-通常是网络图,OP没有提到更新。我将更新我的答案,但我不认为最初的方法是错误的,我认为它的形式很差仅仅因为你不喜欢这种方法就投否决票。不确定它的形式是否糟糕。我仍在尝试让自己感觉到投反对票。我的理由是,虽然你说在有限的示例中技术上这样做是正确的,但它鼓励了一种通常不起作用的编码实践。我的经验是,数据连接正在进行中这是d3中被图书馆新手最广泛误解的部分之一,因此我觉得答案应该特别严格,以显示鼓励和促进对这种基本机制的普遍理解的数据连接技术。当然,这只是我的意见。那么,否决与否取决于你,我可以理解你的观点-我我猜分歧在于我最初的回答是否真的在推广不良做法,或者在有限的范围内是否完全合理。无论是哪种方式,如果你在回应你的建议时更新了答案(因为它不再令人反感),如果你放弃投反对票,那将是普遍的感谢。另一种说法是:一般来说,我可能会对彻头彻尾的糟糕做法投反对票,但不是因为缺乏最佳做法(除非OP特别要求最佳做法)。