D3 强制布局应在单击节点时添加节点和链接

Posted

技术标签:

【中文标题】D3 强制布局应在单击节点时添加节点和链接【英文标题】:D3 force layout shall add nodes and links when clicking a node 【发布时间】:2014-11-20 13:08:50 【问题描述】:

我有一个有效的 D3 示例。我使用武力,一切正常。但我有一个小问题。假设我有 4 个节点。看看这张照片:http://i.imgur.com/J0P4I0n.png 现在当我点击节点“AKZO NV”时,我想得到:http://i.imgur.com/fGXVGMd.png 和旧节点和链接。

所以最后我想要 7 个节点。而“AKZO NV”将成为焦点。所有节点仍然有它们的链接,“AKZO NV”应该有两个。我想你现在知道我想要什么了。

所以我已经有了这个代码。像魅力一样工作,但它没有正确添加新节点和链接。我认为命令的顺序存在一个小问题。

欢迎任何想法:

var alreadyThere = false;
var nodeCircles = ;
var svg, link, node;
var force = d3.layout.force();
var nodes, links;
var width = 700, height = 400;
var boxIDName = "#main-rightinfo";
var JSONFORMERGING;

function createRealGraph(jsonData)
    //console.log(jsonData);
    if (alreadyThere == false)
        JSONFORMERGING=jsonData;
        initializeGraph(jsonData);
    else
        update(JSONFORMERGING.concat(jsonData));
    
    alreadyThere = true;

function update(jsonData) 
    console.log(jsonData);
    jsonData.forEach(function(link) 
      link.source = nodeCircles[link.source] || (nodeCircles[link.source] = name: link.sourceName, ID: link.source, class: link.sourceClass);
      link.target = nodeCircles[link.target] || (nodeCircles[link.target] = name: link.targetName, ID: link.target, class: link.targetClass);
    );

    link = link.data(links);
    link.enter().insert("line")
      .attr("class", "link");

    node = node.data(nodes);
    node.enter().append("g")
        .attr("class", "node")
      .attr("r", 5)
      .call(force.drag);

    force 
        .nodes(d3.values(nodeCircles))
        .links(jsonData)
        .start();
    nodes = force.nodes();
    links = force.links();

function initializeGraph(jsonData)
    jsonData.forEach(function(link) 
      link.source = nodeCircles[link.source] || (nodeCircles[link.source] = name: link.sourceName, ID: link.source, class: link.sourceClass);
      link.target = nodeCircles[link.target] || (nodeCircles[link.target] = name: link.targetName, ID: link.target, class: link.targetClass);
    );

    force 
        .nodes(d3.values(nodeCircles))
        .links(jsonData)
        .size([width, height])
        .linkDistance(60)
        .charge(-200)
        .on("tick", tick)
        .start();

    nodes = force.nodes();
    links = force.links();

    svg = d3.select("#main-right")
        .append("svg")
        .attr("width", width)
        .attr("height", height);
    svg
        .append("svg:defs").selectAll("marker")
        .data(["end"]) 
        .enter().append("svg:marker")   
        .attr("id", String)
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 27)
        .attr("refY", -0.5)
        .attr("markerWidth", 6)
        .attr("markerHeight", 6)
        .attr("orient", "auto")
        .append("svg:path")
        .attr("d", "M0,-5L10,0L0,5")
        .attr('fill', '#00b');

    link = svg.selectAll(".link")
        .data(links)
        .enter().append("line")
        .attr("class", "link")
        .attr("marker-end", "url(#end)");

    node = svg.selectAll(".node")
        .data(nodes)
        .enter().append("g")
        .attr("class", "node")
        .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", function(d) click(d);)
        .on("dblclick", function(d) dblclick(d);)
        .call(force.drag);
    node
        .append("image")
        .attr("xlink:href",  function(d) if (d.class == "Person") return "pics/node_person.png"; else return "pics/node_appln.png"; )
        .attr("x", -20)
        .attr("y", -20)
        .attr("width", 40)
        .attr("height", 40);
    node
        .append("text")
        .attr("x", 19)
        .attr("dy", ".25em")
        .text( function(d) return d.name; );

    function tick() 
      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 + ")"; );
    

【问题讨论】:

不确定我是否明白你想要什么。您的两张图片显示了相同数量的节点——您想在哪里添加新节点以及如何添加? 嗯,你是对的,节点的数量是一样的。但正如我所说,我想将第二张图片添加到第一张图片中。在第一个文档是源。但首先它的一个人是焦点。但是,例如,我不想像那里的 Document 一样松散。好吧,如果仍然不清楚,请说。我再拍一张。 仍然不知道你想要什么——复制所有节点? 我会拍张照片=) 给你,我需要这样的东西i.imgur.com/rMFcrre.png 【参考方案1】:

你只需要测试它是否不是一个对象。这就像一个魅力:

jsonData.forEach(function(link) 
        if (typeof(link.source) != "object")
            link.source = nodeCircles[link.source] || (nodeCircles[link.source] = name: link.sourceName, ID: link.source, class: link.sourceClass);
        
        if (typeof(link.target) != "object")
            link.target = nodeCircles[link.target] || (nodeCircles[link.target] = name: link.targetName, ID: link.target, class: link.targetClass);
         
    );   

【讨论】:

【参考方案2】:

我正在解决同样的问题并得到了“解决方案”。它仍然是一个原型,但也许对你有帮助。

每次鼠标点击节点都会调用一个函数并检测新节点。它们都保存在nodes 中并且都处于活动状态,这意味着显示器上的所有节点都在activeNodes 中。属于从根节点到被点击的路径的所有节点都存储在pathNodes。结果是,只显示活动路径,包括被点击的路径的子节点。

希望它得到清楚的解释。为了更好的理解,请查看源代码:http://github.com/nextlevelshit/d3_nested_nodes

想玩的可以看看:http://dailysh.it/github/d3_nested_nodes/

这里是我的代码的 sn-p:

/**
 * Triggering mouse click start
 */

function mousedown() 

  if (mousedown_node !== null) 

    var pathNodes = findPathNodesTo(mousedown_node);

    var point = d3.mouse(this),
      node = 
        id: nodes.length,
        parent: mousedown_node.id
      ;
    node.x = point[0];
    node.y = point[1];

    var newNodes = findNodesbyParentId(mousedown_node.id),
      startingPoint = 
        x: mousedown_node.x,
        y: mousedown_node.y
      ;

    for (var i = 0; i < pathNodes.length; i++) 
      newNodes.push(pathNodes[i]);
      pathNodes[i].path = true;
    

    var removeNodes = activeNodes.diff(newNodes);
    var addNodes = newNodes.diff(pathNodes).diff(activeNodes);

    for (var i = 0; i < removeNodes.length; i++) 
      removeNode(removeNodes[i].id);
    

    for (var i = 0; i < addNodes.length; i++) 
        addNodes[i].x = startingPoint.x;
        addNodes[i].y = startingPoint.y;
        activeNodes.push(addNodes[i]);
        activeLinks.push(source: findNode(addNodes[i].parent), target: findNode(addNodes[i].id));
    

      // TODO: Find a smoother way do delay popping out new nodes
    );

  

  restart();

最后一个问题是找到一种平滑的方式来弹出新节点...

【讨论】:

以上是关于D3 强制布局应在单击节点时添加节点和链接的主要内容,如果未能解决你的问题,请参考以下文章

D3 强制布局 - 按名称而不是索引链接节点

如何使 D3 中的标签和节点强制布局可点击以导航到 URL?

在版本 4 中将节点动态添加到 D3 Force Layout

d3强制定向布局中的神秘力量?

D3.js 重新启动模拟时节点跳转添加或删除节点

修复 D3 强制定向布局中的节点位置