D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
Pragyagarg
Full window
Github gist
Force Directed Layout
Built with
blockbuilder.org
<!DOCTYPE html> <head> <meta charset="utf-8"> <script src="https://d3js.org/d3.v4.min.js"></script> <style> body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } .grid { stroke: #9A8B7A; stroke-width: 1px; fill: #ce1c28; } .link { stroke: #597761; fill: none; } .node { 1 fill: #EBD8C1; stroke: #9A8B7A; stroke-width: 1px; } circle.active { fill: #FE9922; } path.active { stroke: #FE9922; } circle.source { fill: #93C464; } circle.target { fill: #41A368; } </style> </head> <body> <script> // Feel free to change or delete any of the code you see in this editor! var PromiseWrapper = d => new Promise(resolve => d3.csv(d, p => resolve(p))) Promise.all([PromiseWrapper("nodelist.csv"), PromiseWrapper("edgelist.csv")]) .then(resolve => { createForceLayout(resolve[0], resolve[1]) }) var svg = d3.select("body").append("svg") .attr("width", 960) .attr("height", 500) function createForceLayout(nodes,edges) { var roleScale = d3.scaleOrdinal() .domain(["contractor", "employee", "manager"]) .range(["#75739F", "#41A368", "#FE9922"]) var nodeHash = nodes.reduce((hash, node) => {hash[node.id] = node; return hash; }, {}) edges.forEach(edge => { edge.weight = parseInt(edge.weight) edge.source = nodeHash[edge.source] edge.target = nodeHash[edge.target] }) var linkForce = d3.forceLink() var simulation = d3.forceSimulation() .force("charge", d3.forceManyBody().strength(-40)) .force("center", d3.forceCenter().x(300).y(300)) .force("link", linkForce) .nodes(nodes) .on("tick", forceTick) simulation.force("link").links(edges) d3.select("svg").selectAll("line.link") .data(edges, d => `${d.source.id}-${d.target.id}`) .enter() .append("line") .attr("class", "link") .style("opacity", .5) .style("stroke-width", d => d.weight); var nodeEnter = d3.select("svg").selectAll("g.node") .data(nodes, d => d.id) .enter() .append("g") .attr("class", "node"); nodeEnter.append("circle") .attr("r", 5) .style("fill", d => roleScale(d.role)) nodeEnter.append("text") .style("text-anchor", "middle") .attr("y", 15) .text(d => d.id); nodes.forEach(d => { d.degreeCentrality = edges.filter( p => p.source === d || p.target === d).length }) var marker = d3.select("svg").append('defs') .append('marker') .attr("id", "triangle") .attr("refX", 12) .attr("refY", 6) .attr("markerUnits", 'userSpaceOnUse') .attr("markerWidth", 12) .attr("markerHeight", 18) .attr("orient", 'auto') .append('path') .attr("d", 'M 0 0 12 6 0 12 3 6'); d3.selectAll("line").attr("marker-end", "url(#triangle)"); d3.select("#controls").append("button") .on("click", sizeByDegree).html("Degree Size") function sizeByDegree() { simulation.stop() simulation.force("charge", d3.forceManyBody() .strength(d => -d.degreeCentrality * 20)) simulation.restart() d3.selectAll("circle") .attr("r", d => d.degreeCentrality * 2) } function forceTick() { d3.selectAll("line.link") .attr("x1", d => d.source.x) .attr("x2", d => d.target.x) .attr("y1", d => d.source.y) .attr("y2", d => d.target.y) d3.selectAll("g.node") .attr("transform", d => `translate(${d.x},${d.y})`) } } function filterNetwork() { simulation.stop() var originalNodes = simulation.nodes() var originalLinks = simulation.force("link").links() var influentialNodes = originalNodes.filter(d => d.role === "employee") var influentialLinks = originalLinks.filter(d => influentialNodes.includes(d.source) && influentialNodes.includes(d.target)) d3.selectAll("g.node") .data(influentialNodes, d => d.id) .exit() .transition() .duration(4000) .style("opacity", 0) .remove() d3.selectAll("line.link") .data(influentialLinks, d => `${d.source.id}-${d.target.id}`) .exit() .transition() .duration(3000) .style("opacity", 0) .remove() simulation .nodes(influentialNodes) simulation.force("link") .links(influentialLinks) simulation.alpha(0.1) simulation.restart() } </script> </body>
https://d3js.org/d3.v4.min.js