我有一个按预期工作的力布局图。它从磁盘上的JSON读取图形并显示它。我现在让服务器使用socket io将数据推送到客户端页面。此数据是包含节点和链接数据的JSON。
如何从服务器接收新的JSON时,强制布局刷新并更新布局?
var width = 1900,
height = 1100;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var networkData = {};
var socket = io.connect('http://localhost:3000');
socket.on("networkData", function (data) {
networkData = data; //This is the data I want to use
});
d3.json("data.json", function(error, graph) { //"data.json" is the data
if (error) throw error; // currently being used
// want to use "networkData"
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", "orange")
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
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; });
});
});
修改
我更新了代码,Guillermo Garcia建议如下:
socket.on("networkData", function (data) {
force.stop()
render(data);
force
.nodes(data.nodes)
.links(data.links)
.start();
});
function render(data) {
var link = svg.selectAll(".link")
.data(data.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(data.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", "orange")
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
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; });
});
}
但是,该图仅适用于从服务器发送的第一个networkData
JSON。我还必须手动刷新页面才能显示这些数据。
为什么数据不适用于>第二个数据文件?如何让页面动态更新? (即不必手动刷新)
答案 0 :(得分:1)
您是否尝试过创建渲染函数并在socket.io事件中调用它?
渲染函数应包含从var link = svg.selectAll(".link")
到force.on("tick", function() {
var link = svg.selectAll(".link")
.data(graph.links)
...
...
force.on("tick", function() {
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; });
});
祝你好运!
答案 1 :(得分:0)
对你的情况来说,这可能有点难以实现,但我确信我的代码至少可以给你一些想法。所以这些是我基本上用来清理演示文稿和动态添加新节点的功能。您可以看到结果here。这些行还会检查是否存在重复项并将其过滤掉。如果您需要更多定义或变量,请告诉我。
cleanPresentation: function () {
svg.remove();
nodeCircles = {};
alreadyThere = false;
},
getAlreadyThere: function () {
return alreadyThere;
},
createGraph: function (newJSON) {
if (alreadyThere) {
svg.remove();
nodeCircles = {};
}
this.updateForceUsingNewNodes(this.generateObjects(newJSON));
currentJSON = newJSON;
if (alreadyThere == false) {
this.setbasiczoom();
}
alreadyThere = true;
},
updateGraph: function (newJSON) {
svg.remove();
this.findDuplicatesAndSetEmpty(newJSON);
this.deleteEmptyObjectsInJSON(newJSON);
currentJSON = currentJSON.concat(newJSON);
this.updateForceUsingNewNodes(this.generateObjects(currentJSON));
},
findDuplicatesAndSetEmpty: function (newJSON) {
for (var i = 0; i < currentJSON.length; i++) {
for (var o = 0; o < newJSON.length; o++) {
if ((currentJSON[i].source.ID == newJSON[o].source) && (currentJSON[i].target.ID == newJSON[o].target)
|| (currentJSON[i].source.ID == newJSON[o].target) && (currentJSON[i].target.ID == newJSON[o].source)) {
newJSON[o] = {};
}
}
}
},
deleteEmptyObjectsInJSON: function (json) {
for (var i = 0; i < json.length; i++) {
var y = json[i].source;
if (y === "null" || y === null || y === "" || typeof y === "undefined") {
json.splice(i, 1);
i--;
}
}
},
updateGraphByRemoveElement: function (clickedNode, index) {
svg.remove();
var json4Splicing = currentJSON;
for (var i = 0; i < json4Splicing.length; i++) {
if (json4Splicing[i].source.ID == clickedNode.ID) {
json4Splicing[i] = {};
} else if (json4Splicing[i].target.ID == clickedNode.ID) {
json4Splicing[i] = {};
}
}
familytree.deleteEmptyObjectsInJSON(json4Splicing);
familytree.deleteNode(force.nodes(), clickedNode);
currentJSON = json4Splicing;
familytree.updateForceRemoveElement(familytree.generateObjects(currentJSON));
},
deleteNode: function (allNodes, clickedNode) {
allNodes.forEach(function (node) {
if (node == clickedNode) {
force.links().forEach(function (link) {
if (node.ID == link.source.ID) {
link.target.linkCount--;
}
if (node.ID == link.target.ID) {
link.source.linkCount--;
}
});
node.linkCount = 0;
}
});
},
generateObjects: function (json) {
json.forEach(function (link) {
if (typeof(link.source) == "string") {
link.source = nodeCircles[link.source] || (nodeCircles[link.source] = {name: link.sourceName, significance: link.sourceSign, uniquename: link.sourceUName, ID: link.source, class: link.sourceClass, relation: link.relation, race: link.sourceRace, linkCount: 0});
link.source.linkCount++;
}
if (typeof(link.target) == "string") {
link.target = nodeCircles[link.target] || (nodeCircles[link.target] = {name: link.targetName, significance: link.targetSign, uniquename: link.targetUName, ID: link.target, class: link.targetClass, relation: link.relation, race: link.targetRace, linkCount: 0});
link.target.linkCount++;
}
});
return json;
},
updateForceRemoveElement: function (links) {
force.nodes(d3.values(nodeCircles).filter(function (d) {
return d.linkCount;
}));
force.links(d3.values(links));
familytree.initializeGraph();
},
updateForceUsingNewNodes: function (links) {
force.nodes(d3.values(nodeCircles).filter(function (d) {
return d.linkCount;
}));
force.links(d3.values(links));
this.initializeGraph();
}