这是我第一个堆叠溢出的问题所以如果我犯了一些新手错误,请耐心等待。我在这里搜索了很多问题,并没有找到我正在寻找的东西(在一个案例中我有,但不知道如何实现它)。似乎唯一提出类似问题的人没有得到任何答案。
我用D3创建了一个力布局,事情几乎按照我想要的方式工作。我编辑的两件事情:
1)保持节点不重叠:是的,我已阅读并重新阅读Mike Bostock的集群力布局代码。我不知道如何在我的代码中实现这一点而不会出现严重错误!我从一个教程中尝试了这个代码,但它将我的节点固定在一个角落,并在画布上显示了链接:
var padding = 1, // separation between circles
radius=8;
function collide(alpha) {
var quadtree = d3.geom.quadtree(graph.nodes);
return function(d) {
var rb = 2*radius + padding,
nx1 = d.x - rb,
nx2 = d.x + rb,
ny1 = d.y - rb,
ny2 = d.y + rb;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y);
if (l < rb) {
l = (l - rb) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
你可以看到我的小提琴(下面链接)中的滴答功能的添加注释掉了。
2)包装我的文本标签,使它们适合节点内部。现在,他们在悬停时扩展到节点的全名,但我最终会将其更改为工具提示(一旦我解决了这些问题,我就会找出工具提示) - 现在我只是想要包含在节点内的原始短名称。我已经查看了this answer和this answer(http://jsfiddle.net/Tmj7g/4/)但是当我尝试将其实现到我自己的代码中时,它没有响应或最终聚集了所有节点左上角(??)。
非常感谢任何和所有输入,并且可以在此处编辑我的小提琴:https://jsfiddle.net/lilyelle/496c2bmr/
我也知道我的所有语言都不完全一致或编写D3代码的最简单方式 - 这是因为我已经从不同来源复制和拼接了很多东西,但我仍然在尝试找出为自己写这些东西的最佳方法。对此方面的任何建议也表示赞赏。
答案 0 :(得分:0)
1)碰撞检测:这是一个更新的,有效的jsFiddle,由this example从mbostock引导。添加碰撞检测主要是重要位的复制/粘贴。具体来说,在tick
函数中,我添加了遍历所有节点的代码,如果它们发生碰撞则调整它们的位置:
var q = d3.geom.quadtree(nodes),
i = 0,
n = nodes.length;
while (++i < n) q.visit(collide(nodes[i]));
由于你的jsFiddle没有设置变量nodes
,我把它添加到最后一个剪切的上方:
var nodes = force.nodes()
此外,该循环需要定义函数collide
,就像在Bostock的示例中一样,所以我也将它添加到你的jsFiddle:
function collide(node) {
var r = node.radius + 16,
nx1 = node.x - r,
nx2 = node.x + r,
ny1 = node.y - r,
ny2 = node.y + r;
return function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = node.radius + quad.point.radius;
if (l < r) {
l = (l - r) / l * .5;
node.x -= x *= l;
node.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}
最后一个必要的位来自于检测节点冲突需要知道它们的大小。 Bostock的代码访问上面node.radius
函数内的collide
。您的示例没有设置节点&#39;半径,因此collide
函数node.radius
为undefined
。设置此半径的一种方法是将其添加到json,例如
{radius: 30, "name":"AATF", "full_name":"African Agricultural Technology Foundation", "type":1}
如果您的所有节点都具有相同的半径,那就太过分了。
另一种方法是使用硬编码的数字替换node.radius
的两次出现,例如30
。
我选择在这两个选项之间做一些事情:通过循环加载的json为每个节点分配一个常量node.radius
:
json.nodes.forEach(function(node) {
node.radius = 30;
})
这是让碰撞检测工作的原因。我使用半径30
,因为它是用于渲染这些节点的半径,如.attr("r", 30)
中所示。这将保持所有节点聚集 - 不重叠但仍然相互接触。您可以尝试使用较大的node.radius
值来获得它们之间的空白区域。
2)文字环绕:这是一个艰难的选择。没有简单的方法可以使SVG <text>
包裹在某个宽度上。只有常规的html div
/ span
可以自动执行此操作,但即使是html元素也无法换行以适应圆圈,只能达到恒定的宽度。
您或许能够提出一些妥协方案,以便您始终能够使用某些文字。例如,如果您的数据是全部已知的,并且圆圈的大小始终是相同的固定值,那么您可以提前知道哪些标签可以适合哪些标签,哪些标签不适合。通过在JSON中为每个节点添加short_name
属性,并将其设置为绝对合适的内容,您可以缩短它们。或者,如果事先知道大小和标签,您可以预先确定如何将标签分解为多行并将其硬编码到JSON中。然后,在渲染时,您可以使用多个SVG <text>
元素渲染该文本,并将其手动定位为多行。最后,如果没有提前知道,那么你可以通过切换到将文本渲染为SVG顶部(和外部)的绝对定位div来获得一个好的解决方案,其宽度与圆圈相匹配&#39 ;宽度,以便文本自动换行。