Skip to content

Instantly share code, notes, and snippets.

@cjhin cjhin/data.csv
Last active Mar 6, 2017

Embed
What would you like to do?
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.