Javascript 如何动态更新d3.js force布局图?

Javascript 如何动态更新d3.js force布局图?,javascript,node.js,d3.js,force-layout,Javascript,Node.js,D3.js,Force Layout,我有一个按预期工作的力布局图。它从磁盘上的JSON读取图形并显示它。现在,服务器使用套接字io将数据推送到客户端页面。此数据是包含节点和链接数据的JSON 当force layout从服务器接收到这个新的JSON时,我如何让它刷新和更新布局 var width = 1900, height = 1100; var color = d3.scale.category20(); var force = d3.layout.force() .charge(-120)

我有一个按预期工作的力布局图。它从磁盘上的JSON读取图形并显示它。现在,服务器使用套接字io将数据推送到客户端页面。此数据是包含节点和链接数据的JSON

当force layout从服务器接收到这个新的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。为了显示这些数据,我还必须手动刷新页面


为什么>第二个数据文件的数据不起作用?如何动态更新页面?(即不必手动刷新)

您是否尝试过创建渲染函数并在socket.io事件中调用它

渲染函数应该具有从
var link=svg的所有代码。选择all(“.link”)
force.on(“tick”,function(){


祝你好运!

对于你的案例来说,这可能有点难实现,但我相信我的代码至少可以给你一些想法。因此,这些函数基本上是用来清理演示文稿和动态地从数据库中添加新的节点的。你可以看到结果。这些行还检查这里有重复项并将其过滤掉。如果您需要更多的定义或变量,请告诉我

        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();
        }
cleanPresentation:函数(){
svg.remove();
nodeCircles={};
alreadyThere=假;
},
getAlreadyThere:函数(){
回到这里;
},
createGraph:function(newJSON){
如果(已在此处){
svg.remove();
nodeCircles={};
}
this.updateForceUsingNewNodes(this.generateObject(newJSON));
currentJSON=newJSON;
if(alreadyThere==false){
这个.setbasiczoom();
}
alreadyThere=真;
},
updateGraph:function(newJSON){
svg.remove();
this.findDuplicatesAndSetEmpty(newJSON);
this.deleteEmptyObjectsInJSON(newJSON);
currentJSON=currentJSON.concat(newJSON);
this.updateForceUsingNewNodes(this.GenerateObject(currentJSON));
},
FindDuplicateSetEmpty:函数(newJSON){
for(var i=0;i        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();
        }