forked from cjhin's block: D3-Force: Split Categorical
forked from Thanaporn-sk's block: D3-Force: Split Categorical
forked from pram's block: D3-Force: Split Categorical
| license: mit |
forked from cjhin's block: D3-Force: Split Categorical
forked from Thanaporn-sk's block: D3-Force: Split Categorical
forked from pram's block: D3-Force: Split Categorical
| country | continent | gdp | |
|---|---|---|---|
| Egypt | Africa | 330765 | |
| South Africa | Africa | 312957 | |
| Malaysia | Asia | 296219 | |
| Israel | Asia | 296073 | |
| Denmark | Europe | 294951 | |
| Colombia | South America | 293243 | |
| Singapore | Asia | 292734 | |
| Philippines | Asia | 291965 | |
| Pakistan | Asia | 269971 | |
| Chile | South America | 240222 | |
| Venezuela | South America | 239572 | |
| Ireland | Europe | 238031 | |
| Finland | Europe | 229671 | |
| Portugal | Europe | 199077 | |
| New Zealand | Australia | 172248 |
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <script src="//d3js.org/d3.v4.min.js"></script> | |
| <script> | |
| d3.csv("data.csv", function(error, data) { | |
| //////////////////////// | |
| //////////////////////// | |
| // Everything unique to this bl.ock is in this function: | |
| function categoricalSplit() { | |
| // Create a scale to translate from categorical (string) data value | |
| // to a point on the screen (effectively an invisible axis) | |
| var catScale = d3.scalePoint() | |
| .domain(data.map(function(d) { return d['continent']; })) | |
| .range([0, width]) | |
| .padding(0.5); // give some space at the outer edges | |
| // Add some labels to show whats happening with the split groups | |
| var labels = svg.selectAll("text") | |
| .data(catScale.domain()) // heh, scales take care of the unique, so grab from there | |
| .enter().append("text") | |
| .attr("class", "label") | |
| .text(function(d) { return d; }) | |
| .attr("fill", "#DDD") | |
| .attr("text-anchor", "middle") | |
| .attr("x", function(d) { return catScale(d); }) | |
| .attr("y", height / 2.0 - 100); | |
| var xCatForce = d3.forceX(function(d) { | |
| return catScale(d['continent']); | |
| }); | |
| // Interaction with button | |
| var splitState = false; | |
| document.getElementById("split-button").onclick = function() { | |
| if(!splitState) { | |
| // push the nodes towards respective spots | |
| simulation.force("x", xCatForce); | |
| // emphasize labels | |
| labels.attr("fill", "#000"); | |
| } else { | |
| // reset | |
| simulation.force("x", centerXForce); | |
| labels.attr("fill", "#DDD"); | |
| } | |
| // Toggle state | |
| splitState = !splitState; | |
| // NOTE: Very important to call both alphaTarget AND restart in conjunction | |
| // Restart by itself will reset alpha (cooling of simulation) | |
| // but won't reset the velocities of the nodes (inertia) | |
| simulation.alpha(1).restart(); | |
| }; | |
| } | |
| //////////////////////// | |
| //////////////////////// | |
| //////////////////////// | |
| // The rest of this file is from: | |
| // https://bl.ocks.org/cjhin/4c990c57b9b05e58d56b396751f9747d | |
| var svg = d3.select("svg"), | |
| width = +svg.attr("width"), | |
| height = +svg.attr("height"); | |
| // "Electric repulsive charge", prevents overlap of nodes | |
| var chargeForce = d3.forceManyBody() | |
| // Keep nodes centered on screen | |
| var centerXForce = d3.forceX(width / 2); | |
| var centerYForce = d3.forceY(height / 2); | |
| // Apply default forces to simulation | |
| var simulation = d3.forceSimulation() | |
| .force("charge", chargeForce) | |
| .force("x", centerXForce) | |
| .force("y", centerYForce); | |
| var node = svg.selectAll("circle") | |
| .data(data) | |
| .enter().append("circle") | |
| .attr("r", 10) | |
| .attr("fill", "#777"); | |
| // Add the nodes to the simulation, and specify how to draw | |
| simulation.nodes(data) | |
| .on("tick", function() { | |
| // The d3 force simulation updates the x & y coordinates | |
| // of each node every tick/frame, based on the various active forces. | |
| // It is up to us to translate these coordinates to the screen. | |
| node.attr("cx", function(d) { return d.x; }) | |
| .attr("cy", function(d) { return d.y; }); | |
| }); | |
| // Call the function unique to this block | |
| categoricalSplit(); | |
| }); | |
| </script> | |
| <style> | |
| html { | |
| font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; | |
| } | |
| #split-button{ | |
| position: absolute; | |
| bottom: 10px; | |
| right: 10px; | |
| padding: 10px 20px; | |
| font-size: 2em; | |
| text-align: center; | |
| background: #FFF; | |
| border-radius: 5px; | |
| border: 1px solid #DDD; | |
| } | |
| #split-button:hover { | |
| background: #CCC; | |
| cursor: pointer; | |
| } | |
| </style> | |
| <body> | |
| <div id="split-button">Toggle Split</div> | |
| <svg width="960" height="500"></svg> | |
| </body> | |