This force directed graph has (will have) the following features:
- SVG & force layer fills the parent container
- Nodes confined to container
- Zoomable
- Resize to fill screen
- Context menu
- Hover popup
- Node icons
| { | |
| "nodes":[ | |
| {"name":"Luke","group":1}, | |
| {"name":"Leia","group":1}, | |
| {"name":"Han","group":1}, | |
| {"name":"Chewie","group":1}, | |
| {"name":"Lando","group":1}, | |
| {"name":"Palpatine","group":2}, | |
| {"name":"Maul","group":2}, | |
| {"name":"Boba Fett","group":2}, | |
| {"name":"Greedo","group":2}, | |
| {"name":"Jabba","group":3}, | |
| {"name":"Jawa","group":4}, | |
| {"name":"Jawa","group":4}, | |
| {"name":"Jawa","group":4}, | |
| {"name":"Jawa","group":4}, | |
| {"name":"Jawa","group":4}, | |
| {"name":"Ewok","group":5}, | |
| {"name":"Ewok","group":5}, | |
| {"name":"Ewok","group":5}, | |
| {"name":"Ewok","group":5}, | |
| {"name":"Ewok","group":5}, | |
| {"name":"ObiWan","group":1}, | |
| {"name":"R2D2","group":1}, | |
| {"name":"C3PO","group":1}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Stormtrooper","group":6}, | |
| {"name":"Salacious Crumb","group":3}, | |
| {"name":"Malakili","group":3}, | |
| {"name":"Rancor","group":3} | |
| ], | |
| "links":[ | |
| {"source":1,"target":0,"group":1}, | |
| {"source":1,"target":2,"group":1}, | |
| {"source":2,"target":3,"group":1}, | |
| {"source":2,"target":4,"group":1}, | |
| {"source":6,"target":5,"group":2}, | |
| {"source":7,"target":5,"group":2}, | |
| {"source":8,"target":5,"group":2}, | |
| {"source":10,"target":11,"group":4}, | |
| {"source":10,"target":12,"group":4}, | |
| {"source":10,"target":13,"group":4}, | |
| {"source":10,"target":14,"group":4}, | |
| {"source":15,"target":16,"group":5}, | |
| {"source":15,"target":17,"group":5}, | |
| {"source":15,"target":18,"group":5}, | |
| {"source":15,"target":19,"group":5}, | |
| {"source":20,"target":0,"group":1}, | |
| {"source":21,"target":0,"group":1}, | |
| {"source":22,"target":1,"group":1}, | |
| {"source":23,"target":24,"group":6}, | |
| {"source":24,"target":25,"group":6}, | |
| {"source":25,"target":26,"group":6}, | |
| {"source":26,"target":27,"group":6}, | |
| {"source":27,"target":28,"group":6}, | |
| {"source":28,"target":29,"group":6}, | |
| {"source":9,"target":30,"group":3}, | |
| {"source":9,"target":31,"group":3}, | |
| {"source":31,"target":32,"group":3} | |
| ] | |
| } |
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| #svg-container { | |
| height: 500px; | |
| } | |
| svg { | |
| height: 100%; | |
| width: 100%; | |
| } | |
| .node { | |
| stroke: #fff; | |
| stroke-width: 1.5px; | |
| } | |
| .node text { | |
| stroke: #000; | |
| stroke-width: 0; | |
| font-family: sans-serif; | |
| font-size: .7em; | |
| } | |
| .link { | |
| stroke: #999; | |
| stroke-opacity: .6; | |
| } | |
| </style> | |
| <body> | |
| <div id="svg-container"></div> | |
| <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> | |
| <script src="//d3js.org/d3.v3.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script> | |
| <script> | |
| var color = d3.scale.category20(); | |
| var force = d3.layout.force() | |
| .gravity(0.1) | |
| .charge(-150) | |
| .chargeDistance(500) | |
| .linkDistance(50); | |
| var svg = d3.select("#svg-container").append("svg") | |
| .attr("width", function(){ return $(this).outerWidth(); }) | |
| .attr("height", function(){ return $(this).outerHeight(); }) | |
| d3.json("data.json", function(error, graph) { | |
| if (error) throw error; | |
| nodes = graph.nodes | |
| links = graph.links | |
| force | |
| .size([$('svg').outerWidth(),$('svg').outerHeight()]) | |
| .nodes(graph.nodes) | |
| .links(graph.links) | |
| .start(); | |
| var link = svg.selectAll(".link") | |
| .data(graph.links) | |
| .enter() | |
| .append("line") | |
| .attr("class", "link") | |
| .style("stroke-width", 1) | |
| .style("stroke", function(d) { return color(d.group); }); | |
| var node = svg.selectAll(".node") | |
| .data(graph.nodes) | |
| .enter() | |
| .append("g") | |
| .attr("class", "node") | |
| .call(force.drag); | |
| node.append("circle") | |
| .attr("r",5) | |
| .style("fill", function(d) { return color(d.group); }); | |
| node.append("title") | |
| .text(function(d) { return d.name; }); | |
| node.append("text") | |
| .attr("dx", 12) | |
| .attr("dy", ".35em") | |
| .attr("class","node-label") | |
| .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; }); | |
| /* | |
| For now, the parents are assumed to be the first item in a group. | |
| */ | |
| var parentNodes = _.uniq(graph.nodes,function(n){ | |
| return n.group; | |
| }); | |
| node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); | |
| }); | |
| }); | |
| </script> |