我是d3.js的新手,我目前遇到了问题。我正在使用强制有向图来显示我的数据的关系。这应该允许用户将新节点添加到现有图形并在2个或更多节点之间绘制关系链接。我的警告是我的数据是从ajax调用填充的,我将其分配给变量并将其传递给生成图形的函数。数据的初始加载效果很好,一切都正常显示。我的问题是当用户单击按钮添加新节点时。在该操作上,我正在进行ajax调用以检索新的未链接关系以添加到图形中。我将新检索的数据添加到nodes数组并尝试重新绘制整个图形。但是我在x& y属性设置为NaN。我相信这与forceSimulation如何分配这些值有关。我确实尝试过使用simulation.reset(),但是没有成功。
以下是我的一些代码;
初始调用以检索所有现有关系。
function getGraphData(){
$.ajax({
url: [link to rest uri],
type: 'GET',
contentType: 'application/json'
}).done(function(response){
drawGraph(response);
})
};
这是我第二次要求检索新的未链接关系
function getNewRelationshipData(){
$.ajax({
url: [link to second rest uri],
type: 'GET'
contentType: 'application/json'
}).done(function(response){
var newNode = response.nodes;
updateGraph();
//---same as getGraphData()
$.ajax({
url: [link to rest uri],
type: 'GET',
contentType: 'application/json'
}).done(function(response){
var graphData = response;
graphData.nodes[graphData.nodes.length] = newNode[0]
//assigned relationship data to graphData and appended the newNode value
drawGraph(graphData);
})
});
};
function updateGraph(){
// clears out old graph
d3.selectAll("svg > *").remove();
};
这就是我设置图表的方式。
function drawGraph(relationships){
var svg = d3.select("svg"),
w = +svg.attr("width"),
h = +svg.attr("height);
var g = svg.append("g");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(60))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(w / 2, h / 2))
.force("attraceForce",d3.forceManyBody().strength(-900));
var opacity = 0.05;
var transitionPeriod = 500;
var graph = relationships;
var link = g.selectAll("line")
.data(graph.links)
.enter().append("line")
.style("stroke-width", function(d) { return d.value; })
.style("stroke", "#999" )
.style("opacity", "1")
.attr("group",function(d) {return d.group; })
.on("click", function(d) {
// This is to toggle visibility - need to do it on the nodes, links & text
d3.selectAll("line:not([group='"+d.group+"'])")
.transition().duration(transitionPeriod).style("opacity", function() {
var currentDisplay = d3.select(this).style("opacity");
currentDisplay = currentDisplay == "1" ? opacity : "1";
return currentDisplay;
});
d3.selectAll("circle:not([group='"+d.group+"'])")
.transition().duration(transitionPeriod).style("opacity",function() {
var currentDisplay = d3.select(this).style("opacity");
currentDisplay = currentDisplay == "1" ? opacity : "1";
return currentDisplay;
});
d3.selectAll("text:not([group='"+d.group+"'])")
.transition().duration(transitionPeriod).style("opacity",function() {
var currentDisplay = d3.select(this).style("opacity");
currentDisplay = currentDisplay == "1" ? opacity : "1";
return currentDisplay;
});
})
var node = g
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 14)
.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
var images = g.selectAll("image")
.data(graph.nodes)
.enter().append("image")
.attr("xlink:href",function(d){
var type = d.type,
typeIcon = "",
switch(type){
//assigns an image based on the subject type person, address, phone, ect.
}
return typeIcon;
})
// This is the label for each node
var text = g.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx",12)
.attr("dy",".35m")
.text(function(d) { return d.id;})
.attr("text-anchor", "middle")
.attr("group",function(d) {return d.group;} ) ;
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.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; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
text
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
});
//Used to drag the graph round the screen
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
// This is the zoom handler
var zoom_handler = d3.zoom()
.scaleExtent([1/4, 4])
.on("zoom", zoom_actions);
//specify what to do when zoom event listener is triggered
function zoom_actions(){
g.attr("transform", d3.event.transform);
}
// initial scaling on the svg container - this means everything in it is scaled as well
svg.call(zoom_handler)
.call(zoom_handler.transform, d3.zoomIdentity.scale(0.9,0.9))
;
zoom_handler(svg);
};
我的ajax数据看起来像这样
{
"nodes":[
{"id": "1", "group": "1", "type": "person", "name":"Jon Doe"},
{"id": "2", "group": "1", "type": "person", "name":"Jane Doe"}
//ect list of ~50
],
"links":[
{"source": "1", "target":"2"},
//ect list of ~50
]
}
我希望有更多d3.js经验的人能指出我正确的方向。
答案 0 :(得分:0)
我发布此信息以防其他人遇到同样的问题。我通过将drawGraph函数分解为更小的小部件来解决我的问题。
我将以下内容移到了父范围。
var svg = d3.select("svg"),
w = +svg.attr("width"),
h = +svg.attr("height),
node,
link;
var simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(60))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(w / 2, h / 2))
.force("attraceForce",d3.forceManyBody().strength(-900));
var color = d3.scaleOrdinal(d3.schemeCategory20);
然后在drawGraph函数中我做了以下更改。
function drawGraph(nodes,links){
var g = svg.append("g");
link = g.selectAll(".link").data(links,function(d){ return d.target.id; })
link = link.enter()
.append("line")
.attr("class","link")
.style("stroke-width", function(d) { return d.value; })
.style("stroke", "#999")
node = g.selectAll(".node").data(nodes,function(d){ return d.id; })
node = node.enter()
.append("g")
.attr("class","node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
node.append("circle").attr("r", 14).attr("fill",function(d){return color(d.group);})
node.append("text").attr("dy", -15)
.text(function(d){ return d.id; })
.attr("text-anchor","middle")
.style("fill", "#555");
node.append("image")
.attr("xlink:href",function(d){
var type = d.type,
typeIcon = "",
switch(type){
//assigns an image based on the subject type person, address, phone, ect.
}
return typeIcon;
})
.attr("x", -8)
.attr("y", -8)
.attr("height", 16)
.attr("width", 16);
simulation.nodes(nodes).on("tick", ticked);
simulation.force("link").links(links);
function zoom_actions(){
g.attr("transform", d3.event.transform);
};
var zoom_handler = d3.zoom()
.scaleExtent([1/4, 4])
.on("zoom", zoom_actions);
svg.call(zoom_handler).call(zoom_handler.transform, d3.zoomIdentity.scale(0.9,0.9));
zoom_handler(svg);
};
function ticked() {
link
.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; });
node
.attr("transform", function(d) {
return "translate("+ d.x + ", " + d.y + ")";
});
};
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
然后我添加了以下用于设置数据和绘制图形的函数。
function formatGraphData(relationships){
nodes = relationships.nodes;
links = relationships.links;
simulation.alpha(0.5).restart(); //<- needed to restart simulation and position nodes
drawGraph(nodes,links);
}
然后更新了ajax调用以使用formatGraphData而不是drawGraph。
我将以下内容添加到我的css文件
.links line{
stroke: #999;
}
.nodes circle{
stroke: #fff;
stroke-width: 1.5px;
}