D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
cjhin
Full window
Github gist
D3-Force: Split Categorical
<!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>
https://d3js.org/d3.v4.min.js