D3.js力导向图-边缘标签位置/旋转

10

我对D3.js还比较陌生,最近一直在尝试使用力导向图。其中之一的尝试是在链接上放置标签。

一种方法是通过添加svg:text并手动计算translaterotate来实现,这对于直线可以正常工作。但是,如果链接是一个svg:path(例如弧形),则无法按预期工作。在这些情况下,建议使用svg:textPath解决。

此演示中,您可以看到通过svg:textPath将标签添加到链接的简单实现。唯一的问题是,在源位于目标的右侧时,文本会以相反的方向呈现(从我们的角度来看,从路径的角度来看仍然正确)。我的问题是如何处理这个问题?

我想出的唯一“解决方案”是,在描述的情况下手动交换源和目标。在这里,您可以看到它几乎能够工作。

enter image description here

当交换发生时,您还可以看到弧形翻转到另一侧,这看起来不太正确。 :(


你不能为 textPath 指定文本方向 -- 文本始终遵循路径的方向。因此,你现在的解决方案是唯一可行的。 - Lars Kotthoff
1个回答

10

@LarsKotthoff正确指出textPath必须跟随路径方向。在这种情况下,路径的方向不仅定义了弧线方向,而且还定义了箭头标记附着在端点上的位置 - 这使得在飞行中交换方向变得棘手,因为您还必须移动标记。

更简单的解决方案(尽管如果您有大量链接可能不是最佳选择)是使用一个不可见路径“shadow”真实的链接路径,仅用于文本:

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");

现在你有一个独立的路径可以正确地操作。正如你所注意到的,有两个问题 - 你必须改变路径的方向和弧线的方向。似乎你可以通过交换 sweep-flag 值 (参见文档) 在路径命令字符串中完成这个操作,所以你可以将 Arx,ry 0 0,1 改为 Arx,ry 0 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);
});

查看工作代码:http://jsfiddle.net/nrabinowitz/VYaGg/2/


在数据连接中,不建议为每个数据元素创建多个节点。如果您更新数据并再次进行连接,则会导致意外行为。以下是更多详细信息:https://dev59.com/J2gv5IYBdhLWcg3wKtlB - Scott Cameron
理解了,但如果您不打算以后动态更新数据(通常在网络图中是这种情况),这样做是完全可以接受的,而且OP没有提到更新。我会更新我的答案,但我认为原始方法并不是错误的,因为您不喜欢这种方法而进行投票是不好的形式。 - nrabinowitz
不确定这是否是不好的形式。我仍在努力了解在SO中投票的感觉。我的理由是,虽然你是对的,在有限的示例中这样做在技术上是有效的,但它鼓励了一种在一般情况下不起作用的编码实践。我的经验是,数据连接是d3中最被新手误解的部分之一,因此我认为答案应该特别严谨地展示数据连接技术,以鼓励和促进这种基本机制的普遍理解。当然,这只是我的意见。 - Scott Cameron
好的,给答案点赞或者踩是由你决定的,我能理解你的观点 - 我猜分歧在于我的原始回答是否真正促进了不良实践,还是在有限的范围内完全合理。无论哪种情况,如果答案根据你的建议进行了更新(因为它不再令人反感),那么请不要再踩它,这通常是受欢迎的做法。 - nrabinowitz
2
换句话说:通常情况下,我可能会因为明显的不良实践而投反对票,但不会因为缺乏最佳实践而投反对票(除非 OP 特别要求最佳实践)。 - nrabinowitz
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接