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> |