forked from cjhin's block: D3-Force: Split Continuous
xxxxxxxxxx
<meta charset="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
d3.csv("data.csv", function(data) {
////////////////////////
////////////////////////
// Everything unique to this bl.ock is in this function:
function continuousSplit() {
// Create a scale to translate from continuous (numeric) data value
// to a point on the screen (effectively an invisible axis)
var continuousScale = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d['gdp']; }))
.range([width * 0.1, width * 0.9]); // multiply to give some padding on edge of screen
// Add some labels to show whats happening with the continuous split
var labels = svg.selectAll("text")
.data(continuousScale.domain()) // heh, scales take care of the unique, so grab from there
.enter().append("text")
.attr("class", "label")
.text(function(d) { return d.toLocaleString(); }) // format
.attr("fill", "#DDD")
.attr("text-anchor", "middle")
.attr("x", function(d) { return continuousScale(d); })
.attr("y", height / 2.0 - 100);
var xContinuousForce = d3.forceX(function(d) {
return continuousScale(d['gdp']);
});
// Interaction with button
var splitState = false;
document.getElementById("split-button").onclick = function() {
if(!splitState) {
// push the nodes towards respective spots
simulation.force("x", xContinuousForce);
// 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
continuousSplit();
});
</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>
https://d3js.org/d3.v4.min.js