我有一堆静态圆圈,我想用线条连接它们(这是一个依赖图)。我看到的所有例子都是用d3的现成布局完成的,我不知道如何有效地处理这个问题。我还想在鼠标悬停在该节点上时突出显示与节点相关的线条,以及淡化任何其他形状/线条。
这就是我现在所拥有的:(它只是根据给定的区域大小绘制均匀间隔和大小的圆圈)
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
</head>
<body style="overflow: hidden;">
<div id="drawarea" style="overflow: hidden;"></div>
<script type="text/javascript">
var dataset = [],
i = 0;
for(i=0; i<45; i++){
dataset.push(Math.round(Math.random()*100));
}
var width = 5000,
height = 3000;
var svg = d3.select("#drawarea").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
.append("g");
var div_area = width*height,
num_nodes = dataset.length,
node_area = div_area/num_nodes*0.7,
node_to_padding_ratio = 0.50,
node_dia_inc_pad = Math.sqrt(node_area),
node_radius_wo_pad = node_dia_inc_pad/2*node_to_padding_ratio,
node_padding = node_dia_inc_pad/2*(1-node_to_padding_ratio),
nodes_in_width = parseInt(width/(node_dia_inc_pad)),
nodes_in_height = parseInt(height/(node_dia_inc_pad));
svg.selectAll("circle")
.data(dataset)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", node_radius_wo_pad)
.attr("cx", function(d, i){ return 2*node_radius_wo_pad+i%nodes_in_width*node_dia_inc_pad;})
.attr("cy", function(d, i){ return 2*node_radius_wo_pad+(parseInt(i/nodes_in_width))*node_dia_inc_pad})
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function(){d3.select(this).style("fill", "white");})
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
</script>
</body>
</html>
编辑:我修改后的代码:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
</head>
<body style="overflow: hidden;">
<div id="canvas" style="overflow: hidden;"></div>
<script type="text/javascript">
var graph = {
"nodes":[
{"name":"Myriel","group":1},
{"name":"Napoleon","group":1}
],
"links":[
{"source":1,"target":0,"value":1}
]
}
var width = 2000,
height = 1000;
var svg = d3.select("#canvas").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
.append("g");
var div_area = width*height,
num_nodes = graph.nodes.length,
node_area = div_area/num_nodes,
node_to_padding_ratio = 0.50,
node_dia_inc_pad = Math.sqrt(node_area),
node_radius_wo_pad = node_dia_inc_pad/2*node_to_padding_ratio,
node_padding = node_dia_inc_pad/2*(1-node_to_padding_ratio),
nodes_in_width = parseInt(width/(node_dia_inc_pad)),
nodes_in_height = parseInt(height/(node_dia_inc_pad));
var xScale = d3.scale.linear()
.domain([0,nodes_in_width])
.range([node_radius_wo_pad,width-node_radius_wo_pad]);
var yScale = d3.scale.linear()
.domain([0,nodes_in_height])
.range([node_radius_wo_pad,height-node_radius_wo_pad]);
var lines = svg.attr("class", "line")
.selectAll("line").data(graph.links)
.enter().append("line")
.attr("x1", function(d) { return xScale(d.source%nodes_in_width); })
.attr("y1", function(d) { return yScale(parseInt(d.source/nodes_in_width)); })
.attr("x2", function(d) { return xScale(d.target%nodes_in_width); })
.attr("y2", function(d) { return yScale(parseInt(d.target/nodes_in_width)); })
.attr("src", function(d) { return d.source; })
.attr("trgt", function(d) { return d.target; })
.style("stroke", "grey");
var circles = svg.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", node_radius_wo_pad)
.attr("cx", function(d, i){ return xScale(i%nodes_in_width);})
.attr("cy", function(d, i){ return yScale(parseInt(i/nodes_in_width));})
.attr("index", function(d, i){return i;})
.on("mouseover", function(){
var that = this;
lines.filter(function() {
return d3.select(this).attr("src") == d3.select(that).attr("index");
}).style("stroke", "red");
lines.filter(function() {
return d3.select(this).attr("trgt") == d3.select(that).attr("index");
}).style("stroke", "green");
lines.filter(function() {
return (d3.select(this).attr("trgt") != d3.select(that).attr("index") && d3.select(this).attr("src") != d3.select(that).attr("index"));
}).style("display", "none");
d3.select(this).style("fill", "aliceblue");
})
.on("mouseout", function(){
lines.style("stroke", "grey")
.style("display", "block");
d3.select(this).style("fill", "white");
});
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
</script>
</body>
</html>
我现在要做的是让线条指向的圆圈和类似的颜色。我不知道如何从圆圈的“鼠标悬停”事件中引用它们。会做一些测试......
答案 0 :(得分:5)
您尚未指定节点的连接方式,因此我假设所有内容都与所有内容相关联。原则与任何其他布局相同 - 您获取确定链接的数据并将其传递给.data()
。在你的代码中,坐标不是数据的一部分,这使得它更冗长,但仍然非常简单。
要添加链接,我正在使用nested selection - 我为每个节点添加了g
元素,并在与所有其他节点的连接下面。
var lines = svg.selectAll("g.line").data(dataset)
.enter().append("g").attr("class", "line")
.selectAll("line").data(dataset)
.enter().append("line")
.attr("x1", function(d, i) { return 2*node_radius_wo_pad+i%nodes_in_width*node_dia_inc_pad; })
.attr("y1", function(d, i) { return 2*node_radius_wo_pad+(parseInt(i/nodes_in_width))*node_dia_inc_pad; })
.attr("x2", function(d, i, j) { return 2*node_radius_wo_pad+j%nodes_in_width*node_dia_inc_pad; })
.attr("y2", function(d, i, j) { return 2*node_radius_wo_pad+(parseInt(j/nodes_in_width))*node_dia_inc_pad; });
这为每对节点添加一行。请注意,它将在相同节点(您将无法看到)之间添加链接,并在每对节点之间添加2个链接 - 一次从一个节点开始,一次在另一个节点开始。我没有在这里过滤掉这些案例以保持代码简单。在您的特定应用程序中,我猜测无论如何都以另一种方式确定连接。
要突出显示在突出显示时连接特定节点的链接,我使用包含所有节点的links
变量,并过滤掉其起始坐标与圆坐标不同的变量。然后将过滤后的选择涂成红色。
.on("mouseover", function(){
var that = this;
lines.filter(function() {
return d3.select(this).attr("x1") == d3.select(that).attr("cx") && d3.select(this).attr("y1") == d3.select(that).attr("cy");
}).style("stroke", "red");
d3.select(this).style("fill", "aliceblue");
})
如果坐标是数据的一部分,那么一切都会变得容易一些,看起来更像是你在力布局中看到的例子。我建议创建一个与您的链接一样的数据结构,使用source
和target
属性来确定源节点和目标节点。
完整示例here。