简体   繁体   English

更新d3强制布局中的现有节点

[英]Update existing nodes in a d3 Force Layout

I have created a force layout using d3 and it works well. 我使用d3创建了一个力布局,效果很好。 My initial data is loaded from a json file and the chart is drawn with techniques that are similar to this d3.js example : 我的初始数据是从json文件加载的,并且使用与此d3.js示例类似的技术绘制图表:

在此输入图像描述

Now that the chart is on the screen I need to add, update and remove nodes on the fly from data I receive over a web-socket. 现在图表在屏幕上,我需要通过Web套接字从我收到的数据中动态添加,更新和删除节点。 I have add and remove methods working, but I can't find the correct way to update an existing node's properties. 我添加和删除方法工作,但我找不到更新现有节点属性的正确方法。

From the reading I have undertaken I gather the correct technique is to change the data source, then update the chart by using the enter() method. 从我所进行的阅读中我收集正确的技术是更改数据源,然后使用enter()方法更新图表。

To update a node I am doing the following: 要更新节点,我正在执行以下操作:

function updateNode(id, word, size, trend, parent_id){    
  var updateNode = nodes.filter(function(d, i) { return d.id == id ? this : null; });
  if(updateNode[0]){   
    updateNode.size = Number(size);
    updateNode.trend = trend;
    nodes[updateNode.index] = updateNode;      
    update();
  }
}

The update function then updates the nodes with: 然后,更新功能使用以下命令更新节点:

function update(){
  node = vis.selectAll('.node')
  .data(nodes, function(d) {
    return d.id;
  })

  createNewNodes(node.enter());
  node.exit().remove();
  force.start();
}

function createNewNodes(selection){    
  var slct = selection.append('g')
  .attr('class', 'node')
  .call(force.drag);

  slct.append('circle')
    .transition()
    .duration(500)
    .attr('r', function(d) {
      if(d.size){
        return Math.sqrt(sizeScale(d.size)*40);
      }
   })
}

Am I taking the right approach to this? 我采取了正确的方法吗? When I try this code the node I get as the datum when trying to set the radius attribute on the circle is the last node in the nodes array. 当我尝试这个代码时,我尝试在圆上设置radius属性时获得的节点是节点数组中的最后一个节点。 Ie the one that contains the hierarchical node data rather than a single node object. 即包含分层节点数据而不是单个节点对象的那个。

Any pointers would be greatly appreciated, I've spent too much time on this :) 任何指针都会非常感激,我花了太多时间在这:)

There are multiple points that you need. 您需要多个点。 What I get from your questions is: 'how do I use the reusable pattern' 我从你的问题中得到的是:'我如何使用可重复使用的模式'

The simple answer to this question, would be to tell you to read this excellent tutorial from Mike Bostock: towards reusable charts 这个问题的简单答案就是告诉你阅读Mike Bostock的这篇优秀教程: 对可重复使用的图表

If you want to have more information, this selection of documents can be interesting: 如果您想获得更多信息,这些文档选择可能很有趣:

Now, here is a draft of the implementations I would do for your particular issue: 现在,这是我将针对您的特定问题执行的草案:

function ForceGraph(selector,data) {
   // This is the class that will create a graph

   var _data = data  
   // Local variable representing the forceGraph data 

   svg = d3.select(selector)
      .append('svg')
   // Create the initial svg element and the utilities you will need. 
   // These are the actions that won't be necessary on update. 
   // For example creating the axis objects, your variables, or the svg container

   this.data = function(value) {
      if(!arguments.length) {
         // accessor
         return _data;
      }
      _data = value;
      return this;
      // setter, returns the forceGraph object
   }

   this.draw = function() {
      // draw the force graph using data

      // the method here is to use selections in order to do: 
      // selection.enter().attr(...) // insert new data
      // selection.exit().attr(...) // remove data that is not used anymore
      // selection.attr(...) // 

   }
}

var selector = "#graphId";
var data = {nodes: [...],links: [...]};
myGraph = new ForceGraph(selector,data);
// Create the graph object
// Having myGraph in the global scope makes it easier to call it from a json function or anywhere in the code (even other js files). 
myGraph.draw();
// Draw the graph for the first time 

// wait for something or for x seconds  

var newData = {nodes: [...],links: [...]};
// Get a new data object via json, user input or whatever
myGraph.data(newData);
// Set the graph data to be `newData`
myGraph.draw();
// redraw the graph with `newData`

As you can probably see, the goal is not to have a function to add a new node. 您可能已经看到,目标不是具有添加新节点的功能。 The goal is to redraw the entire force directed graph by only updating or removing existing nodes and adding the new nodes. 目标是仅通过更新或删除现有节点并添加新节点来重绘整个力导向图。 This way the drawing code is written only once, then only the data changes. 这样绘图代码只写一次,然后只有数据发生变化。

For further reading, this question was my goldmine when I first tackled this issue: Updating links on a force directed graph from dynamic json data 为了进一步阅读,当我第一次解决这个问题时,这个问题是我的金矿: 从动态json数据更新强制有向图上的链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM