jjzjj

javascript - 在 d3.js 中有 child 朝向多边的树(类似于家谱)

coder 2024-12-31 原文

var treeData = [{
    "name": "Device",
    "parent": "null"
  }

];
var treeData2 = [{
    "name": "Device",
    "parent": "null"
  }

];

$(document).ready(function() {

  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 1260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;

  var i = 0,
    duration = 750,
    root;

  var tree = d3.layout.tree()
    .size([height, width]);

  var diagonal = d3.svg.diagonal()
    .projection(function(d) {
      return [d.y, d.x];
    });

  var svg = d3.select("body").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom);


  makeRightTree();
  makeLeftTree();
});


function makeRightTree() {
  // ************** Generate the tree diagram  *****************
  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 1260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;

  var i = 0,
    duration = 750,
    root;

  var tree = d3.layout.tree()
    .size([height, width]);

  var diagonal = d3.svg.diagonal()
    .projection(function(d) {
      return [d.y, d.x];
    });

  var svg = d3.select("svg").append("g")
    .attr("transform", "translate(600,0)");

  root = treeData[0];
  root.x0 = height / 2;
  root.y0 = 0;

  update(root);



  function update(source) {

    // Compute the new tree layout.
    var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);

    // Normalize for fixed-depth.
    nodes.forEach(function(d) {
      d.y = d.depth * 180;
    });

    // Update the nodes…
    var node = svg.selectAll("g.node")
      .data(nodes, function(d) {
        return d.id || (d.id = ++i);
      });

    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
      .attr("class", function(d) {
        if (d.parent == "null") {
          return "node rightparent" //since its root its parent is null
        } else
          return "node rightchild" //all nodes with parent will have this class
      })
      .attr("transform", function(d) {
        return "translate(" + source.y0 + "," + source.x0 + ")";
      })
      .on("click", click);

    nodeEnter.append("rect")
      .attr("x", "-10")
      .attr("y", "-15")
      .attr("height", 30)
      .attr("width", 100)
      .attr("rx", 15)
      .attr("ry", 15)
      .style("fill", "#f1f1f1");

    nodeEnter.append("text")
      .attr("x", function(d) {
        return d.children || d._children ? -13 : 13;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) {
        return d.children || d._children ? "end" : "start";
      })
      .text(function(d) {
        return d.name;
      })
      .style("fill-opacity", 1e-6);

    var addRightChild = nodeEnter.append("g");
    addRightChild.append("rect")
      .attr("x", "90")
      .attr("y", "-10")
      .attr("height", 20)
      .attr("width", 20)
      .attr("rx", 10)
      .attr("ry", 10)
      .style("stroke", "#444")
      .style("stroke-width", "2")
      .style("fill", "#ccc");

    addRightChild.append("line")
      .attr("x1", 95)
      .attr("y1", 1)
      .attr("x2", 105)
      .attr("y2", 1)
      .attr("stroke", "#444")
      .style("stroke-width", "2");

    addRightChild.append("line")
      .attr("x1", 100)
      .attr("y1", -4)
      .attr("x2", 100)
      .attr("y2", 6)
      .attr("stroke", "#444")
      .style("stroke-width", "2");


    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + d.y + "," + d.x + ")";
      });

    nodeUpdate.select("circle")
      .attr("r", 10)
      .style("fill", function(d) {
        return d._children ? "lightsteelblue" : "#fff";
      });

    nodeUpdate.select("text")
      .style("fill-opacity", 1);

    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + source.y + "," + source.x + ")";
      })
      .remove();

    nodeExit.select("circle")
      .attr("r", 1e-6);

    nodeExit.select("text")
      .style("fill-opacity", 1e-6);

    // Update the links…
    var link = svg.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });

    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "g")
      .attr("class", "link")
      .attr("d", function(d) {
        var o = {
          x: source.x0,
          y: source.y0
        };
        return diagonal({
          source: o,
          target: o
        });
      }).on("click", removelink);

    function removelink(d) {
      //this is the links target node which you want to remove
      var target = d.target;
      //make new set of children
      var children = [];
      //iterate through the children 
      target.parent.children.forEach(function(child) {
        if (child.id != target.id) {
          //add to teh child list if target id is not same 
          //so that the node target is removed.
          children.push(child);
        }
      });
      //set the target parent with new set of children sans the one which is removed
      target.parent.children = children;
      //redraw the parent since one of its children is removed
      update(d.target.parent)
    }

    // Transition links to their new position.
    link.transition()
      .duration(duration)
      .attr("d", diagonal);

    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {
          x: source.x,
          y: source.y
        };
        return diagonal({
          source: o,
          target: o
        });
      })
      .remove();

    // Stash the old positions for transition.
    nodes.forEach(function(d) {
      d.x0 = d.x;
      d.y0 = d.y;
    });


    addRightChild.on("click", function(d) {

      event.stopPropagation();
      $("#child-info").show();
      $("#child-text").val("");

      $("#btn-add-child").off('click');
      $("#btn-add-child").click(function() {
        var childname = $("#child-text").val();

        if (typeof d.children === 'undefined') {


          var newChild = [{
            "name": childname,
            "parent": "Son Of A",
          }];

          console.log(tree.nodes(newChild[0]));
          var newnodes = tree.nodes(newChild);
          d.children = newnodes[0];
          console.log(d.children);
          update(d);


        } else {
          var newChild = {
            "name": childname,
            "parent": "Son Of A",
          };
          console.log(d.children);
          d.children.push(newChild);
          console.log(d.children);
          update(d);


        }



        $("#child-info").hide();
      });




    });;


  }

  // Toggle children on click.
  function click(d) {

    // console.log(d);
    // if (d.children) {
    //  d._children = d.children;
    //  d.children = null;
    // } else {
    //  d.children = d._children;
    //  d._children = null;
    // }
    // update(d);
  }



}


function makeLeftTree() {
  // ************** Generate the tree diagram  *****************
  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 1260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;

  var i = 0,
    duration = 750,
    root;

  var tree = d3.layout.tree()
    .size([height, width]);

  var diagonal = d3.svg.diagonal()
    .projection(function(d) {
      return [d.y, d.x];
    });

  var svg = d3.select("svg").append("g")
    .attr("transform", "translate(-421,0)");

  root = treeData2[0];
  root.x0 = height / 2;
  root.y0 = width;

  update(root);



  function update(source) {

    // Compute the new tree layout.
    var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);

    // Normalize for fixed-depth.
    nodes.forEach(function(d) {
      d.y = width - (d.depth * 180);
    });

    // Update the nodes…
    var node = svg.selectAll("g.node")
      .data(nodes, function(d) {
        return d.id || (d.id = ++i);
      });

    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
      .attr("class", function(d) {
        if (d.parent == "null") {
          return "node leftparent" //since its root its parent is null
        } else
          return "node leftchild" //all nodes with parent will have this class
      })
      .attr("transform", function(d) {
        return "translate(" + source.y0 + "," + source.x0 + ")";
      })
      .on("click", click);

    nodeEnter.append("rect")
      .attr("x", "-10")
      .attr("y", "-15")
      .attr("height", 30)
      .attr("width", 100)
      .attr("rx", 15)
      .attr("ry", 15)
      .style("fill", "#f1f1f1");

    nodeEnter.append("text")
      .attr("x", function(d) {
        return d.children || d._children ? -13 : 13;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) {
        return d.children || d._children ? "end" : "start";
      })
      .text(function(d) {
        return d.name;
      })
      .style("fill-opacity", 1e-6);

    var addLeftChild = nodeEnter.append("g");
    addLeftChild.append("rect")
      .attr("x", "-30")
      .attr("y", "-10")
      .attr("height", 20)
      .attr("width", 20)
      .attr("rx", 10)
      .attr("ry", 10)
      .style("stroke", "#444")
      .style("stroke-width", "2")
      .style("fill", "#ccc");

    addLeftChild.append("line")
      .attr("x1", -25)
      .attr("y1", 1)
      .attr("x2", -15)
      .attr("y2", 1)
      .attr("stroke", "#444")
      .style("stroke-width", "2");

    addLeftChild.append("line")
      .attr("x1", -20)
      .attr("y1", -4)
      .attr("x2", -20)
      .attr("y2", 6)
      .attr("stroke", "#444")
      .style("stroke-width", "2");




    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + d.y + "," + d.x + ")";
      });

    nodeUpdate.select("circle")
      .attr("r", 10)
      .style("fill", function(d) {
        return d._children ? "lightsteelblue" : "#fff";
      });

    nodeUpdate.select("text")
      .style("fill-opacity", 1);

    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + source.y + "," + source.x + ")";
      })
      .remove();

    nodeExit.select("circle")
      .attr("r", 1e-6);

    nodeExit.select("text")
      .style("fill-opacity", 1e-6);

    // Update the links…
    var link = svg.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });

    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "g")
      .attr("class", "link")
      .attr("d", function(d) {
        var o = {
          x: source.x0,
          y: source.y0
        };
        return diagonal({
          source: o,
          target: o
        });
      }).on("click", removelink);

    function removelink(d) {
      //this is the links target node which you want to remove
      var target = d.target;
      //make new set of children
      var children = [];
      //iterate through the children 
      target.parent.children.forEach(function(child) {
        if (child.id != target.id) {
          //add to teh child list if target id is not same 
          //so that the node target is removed.
          children.push(child);
        }
      });
      //set the target parent with new set of children sans the one which is removed
      target.parent.children = children;
      //redraw the parent since one of its children is removed
      update(d.target.parent)
    }

    // Transition links to their new position.
    link.transition()
      .duration(duration)
      .attr("d", diagonal);

    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {
          x: source.x,
          y: source.y
        };
        return diagonal({
          source: o,
          target: o
        });
      })
      .remove();

    // Stash the old positions for transition.
    nodes.forEach(function(d) {


      d.x0 = d.x;
      d.y0 = d.y;
    });


    addLeftChild.on("click", function(d) {

      event.stopPropagation();
      $("#child-info").show();
      $("#child-text").val("");

      $("#btn-add-child").off('click');
      $("#btn-add-child").click(function() {
        var childname = $("#child-text").val();

        if (typeof d.children === 'undefined') {


          var newChild = [{
            "name": childname,
            "parent": "Son Of A",
          }];

          console.log(tree.nodes(newChild[0]));
          var newnodes = tree.nodes(newChild);
          d.children = newnodes[0];
          console.log(d.children);
          update(d);
        } else {
          var newChild = {
            "name": childname,
            "parent": "Son Of A",
          };
          console.log(d.children);
          d.children.push(newChild);
          console.log(d.children);
          update(d);


        }

        $("#child-info").hide();
      });

    });;


  }

  // Toggle children on click.
  function click(d) {

    // console.log(d);
    // if (d.children) {
    //  d._children = d.children;
    //  d.children = null;
    // } else {
    //  d.children = d._children;
    //  d._children = null;
    // }
    // update(d);
  }
}
#child-info {
  width: 100px;
  height: 50px;
  height: auto;
  position: fixed;
  padding: 10px;
  display: none;
  left: 40%;
}
#btn-add-child {} .control-bar {
  min-height: 50px;
  padding: 10px 0px;
  background: #f1f1f1;
  border-bottom: 1px solid #ccc;
  box-shadow: 0 3px 2px -2px rgba(200, 200, 200, 0.2);
  color: #666;
}
.asset-title {
  padding: 15px;
  float: left;
}
.control-buttons {
  float: right;
  padding: 10px;
}
.control-buttons button {
  border: none;
  border-radius: 0px;
  background: #fff;
  color: #666;
  height: 30px;
  width: 30px;
}
.node {
  cursor: pointer;
}
.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}
.node text {
  font: 12px sans-serif;
}
.link {
  fill: none;
  stroke: #85e0e0;
  stroke-width: 2px;
}
.rightparent>rect {
  display: none;
}
.leftparent>rect {
  fill: #f1f1f1;
  stroke: #ccc;
  stroke-width: 2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<div id="child-info" style="display:none">

  <input type="text" id="child-text" placeholder="child name">

  <button id="btn-add-child">add</button>
</div>

我正在尝试学习 d3.js,目的是制作一个看起来像这样的结构。我可以在哪里动态添加和删除节点,并且我已经部分成功。 (在堆栈溢出中的许多极客的帮助下)。

代码片段如下。通过背靠背放置两棵树可以实现以下效果。一棵树 - 右向和其他左向。两棵树的根节点叠加。

目前的工作如下。

单击相应侧的加号以添加节点,单击链接以删除节点及其子节点。如果您检查工作示例代码片段,它最初可能运行良好。

但是由于两棵树是独立的实体,一旦两边变得高度不对称,两个根节点的位置就会不同,两个节点可见。 (其中一个将被隐藏,但您可以看到从不同点开始的链接 - 要查看此问题,请尝试在一侧添加几个节点,直到它的根节点位置发生变化)。像这样。

现在我的问题

我走错路了吗?有没有办法直接将单个 json 文件转换成这样的结构。我研究了很多,发现 this Cyril 关于制作家谱的绝妙答案.但我想这个案子是不同的。有没有办法直接制作这个结构?

或者有没有一种方法可以根据一个节点的移动将节点一起移动?

非常感谢任何指导。

最佳答案

为此,您需要修复根节点。

这是修复左/右根的方法。

在函数 makeLeftTree 中。

  oldlx = root.x0 = height / 2; //store the center x position
  oldly = root.y0 = width; //store the center y position

在函数 makeRightTree 中。

  oldrx = root.x0 = height / 2; //store the center x position
  oldry = root.y0 = width; //store the center y position

然后在左侧节点更新中,您使用存储的位置更新节点位置。

 var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        if (d.parent =="null"){
          d.y = oldly;
          d.x = oldlx;
        }
        return "translate(" + d.y + "," + d.x + ")";
      });

同样在右侧节点更新中,您使用存储的位置更新节点位置。

var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        if (d.parent =="null"){
          d.y = oldry;
          d.x = oldrx;
        }

        return "translate(" + d.y + "," + d.x + ")";
      });

工作代码 here

关于javascript - 在 d3.js 中有 child 朝向多边的树(类似于家谱),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34684608/

有关javascript - 在 d3.js 中有 child 朝向多边的树(类似于家谱)的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

  2. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  3. ruby - ruby 中有 each_if 吗? - 2

    假设我在Ruby中有这个each循环。@list.each{|i|putsiifi>10breakend}我想循环遍历列表直到满足条件。这让我感到“不像Ruby”,因为我是Ruby的新手,是否有Ruby方法可以做到这一点? 最佳答案 您可以使用Enumerable#detect或Enumerable#take_while,取决于您想要的结果。@list.detect{|i|putsii>10}#Returnsthefirstelementgreaterthan10,ornil.正如其他人所指出的,更好的风格是先进行子选择,然后再对其

  4. ruby-on-rails - "assigns"在 Ruby on Rails 中有什么作用? - 2

    我目前正在尝试学习RubyonRails和测试框架RSpec。assigns在此RSpec测试中做什么?describe"GETindex"doit"assignsallmymodelas@mymodel"domymodel=Factory(:mymodel)get:indexassigns(:mymodels).shouldeq([mymodel])endend 最佳答案 assigns只是检查您在Controller中设置的实例变量的值。这里检查@mymodels。 关于ruby-o

  5. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  6. ruby - 字符串文字前面的 * 在 ruby​​ 中有什么作用? - 2

    这段代码似乎创建了一个范围从a到z的数组,但我不明白*的作用。有人可以解释一下吗?[*"a".."z"] 最佳答案 它叫做splatoperator.SplattinganLvalueAmaximumofonelvaluemaybesplattedinwhichcaseitisassignedanArrayconsistingoftheremainingrvaluesthatlackcorrespondinglvalues.Iftherightmostlvalueissplattedthenitconsumesallrvaluesw

  7. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

  8. ruby-on-rails - Rails - 理解 application.js 和 application.css - 2

    rails新手。只是想了解\assests目录中的这两个文件。例如,application.js文件有如下行://=requirejquery//=requirejquery_ujs//=require_tree.我理解require_tree。只是将所有JS文件添加到当前目录中。根据上下文,我可以看出requirejquery添加了jQuery库。但是它从哪里得到这些jQuery库呢?我没有在我的Assets文件夹中看到任何jquery.js文件——或者直接在我的整个应用程序中没有看到任何jquery.js文件?同样,我正在按照一些说明安装TwitterBootstrap(http:

  9. ruby - Ruby 中有包实现吗? - 2

    是否有bag集合的实现(类似于集合的集合,它会记录对象被插入的次数)? 最佳答案 当然!它也被称为multiset.这是一个nicerubyimplementation. 关于ruby-Ruby中有包实现吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4351793/

  10. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

随机推荐