接受D3布局树

时间:2015-10-13 05:28:12

标签: javascript json d3.js tree

我正在创建一个类似于D3 Layout Tree的树,并试图绑定我的自定义JSON对象。切换树节点的代码如下:

function toggleAll(d) {
    if (d.children) {
      d.children.forEach(toggleAll);
      toggle(d);
    }
  }

  // Initialize the display to show a few nodes.
  root.children.forEach(toggleAll);
  toggle(root.children[1]);

 // Toggle children.
 function toggle(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
}

正如您所看到的,dchildren属性紧密相关 - 我正在尝试重写此函数以接受我的JSON中的任何嵌套对象,以便它也可以切换/扩展。但是我还没有运气(尝试过hasOwnProperty)。有什么想法吗?

编辑:包括我的JSON结构:

{
"date": "10242013"
"id": 34534
"addr": "444 Little Lane"    
"department": {id: 13, addr: "555 ShoeHorse Road", name: "CTS",…}
"manager": {id: 454, addr: "444 Little Lane", name: "Bobby Johnson",…}
...

忽略输入错误的JSON ^此对象可以在这些对象中包含许多嵌套对象和对象。我试图避免硬编码的解决方案,支持:接受所有"方法

1 个答案:

答案 0 :(得分:1)

我已编写代码将您的自定义JSON转换为d3树布局所需的格式。

var json = {
  "date": "10242013",
  "id": "34534",
  "addr": "444 Little Lane",
  "department": {
    "id": 13,
    "addr": "555 ShoeHorse Road",
    "name": "CTS",
    "computer": {
      "id": 56,
      "name": "CT"
    },
    "electronics": {
      "id": 65,
      "name": "EC"
    }
  },
  "manager": {
    "id": 454,
    "addr": "444 Little Lane",
    "name": "Bobby Johnson"
  }
};

function convertJSON(object) {
  for (var key in object) {
    if (typeof(object[key]) == "object") {
      var obj = convertJSON(object[key]);
      obj.key = obj.name ? obj.name : "";
      obj.name = key;
      if (!object["children"])
        object["children"] = [];
      object["children"].push(obj);
      delete object[key];
    }
  }
  return object;
}

json = convertJSON(json);

console.log(json);
var m = [20, 120, 20, 120],
  w = 1280 - m[1] - m[3],
  h = 800 - m[0] - m[2],
  i = 0,
  root;

var tree = d3.layout.tree()
  .size([h, w]);

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

var vis = d3.select("#body").append("svg:svg")
  .attr("width", w + m[1] + m[3])
  .attr("height", h + m[0] + m[2])
  .append("svg:g")
  .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

root = json;
root.x0 = h / 2;
root.y0 = 0;

function toggleAll(d) {
  if (d.children) {
    d.children.forEach(toggleAll);
    toggle(d);
  }
}

// Initialize the display to show a few nodes.
root.children.forEach(toggleAll);

update(root);

function update(source) {
  var duration = d3.event && d3.event.altKey ? 5000 : 500;

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

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

  // Update the nodes…
  var node = vis.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("svg:g")
    .attr("class", "node")
    .attr("transform", function(d) {
      return "translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on("click", function(d) {
      toggle(d);
      update(d);
    });

  nodeEnter.append("svg:circle")
    .attr("r", 1e-6)
    .style("fill", function(d) {
      return d._children ? "lightsteelblue" : "#fff";
    });

  nodeEnter.append("svg:text")
    .attr("x", function(d) {
      return d.children || d._children ? -10 : 10;
    })
    .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);

  // 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", 4.5)
    .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 = vis.selectAll("path.link")
    .data(tree.links(nodes), function(d) {
      return d.target.id;
    });

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

  // 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;
  });
}

// Toggle children.
function toggle(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
}
.node circle {
  cursor: pointer;
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;
}
.node text {
  font-size: 11px;
}
path.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="body">
  <div id="footer">
    d3.layout.tree
    <div class="hint">click or option-click to expand or collapse</div>
  </div>
</div>

其他可能的方法是覆盖treemap的children函数,并迭代d并使用tyepof来查找它是否是孩子。但这并不容易。

在这种情况下,toggle方法可能如下所示。

function toggle(d) {
  for(var key in d){
     if (typeof(d[key])=="object") {
       if((/^_/).test(key)){
          d[key.substr(0)] = d[key];             
       } else{
          d["_"+key] = d[key]; 
       }   
       delete d[key];          
     }
  }
}