Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active February 27, 2017 16:41
Show Gist options
  • Save mbostock/1009139 to your computer and use it in GitHub Desktop.
Save mbostock/1009139 to your computer and use it in GitHub Desktop.
Square Grid
license: gpl-3.0

A simple and effective form for visualizing magnitude is this gridded layout of colored squares. Each individual square represents one unit; each small row represents ten units; the small rows are grouped into ten, representing one hundred; lastly, ten groups per large row represents one thousand. This arrangement affords quick and natural reading of exact counts by powers of ten.

For an example of this technique in practice, see Randall Munroe’s Radiation Dose Chart.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.cells {
fill: #aaa;
}
.label {
text-anchor: start;
font: 24px sans-serif;
}
</style>
<svg width="960" height="990"></svg>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var formatNumber = d3.format(",d");
var svg = d3.select("svg");
var width = +svg.attr("width"),
height = +svg.attr("height");
var groupSpacing = 3,
cellSpacing = 1,
cellSize = Math.floor((width - 11 * groupSpacing) / 100) - cellSpacing,
offset = Math.floor((width - 100 * cellSize - 90 * cellSpacing - 11 * groupSpacing) / 2);
var updateDuration = 125,
updateDelay = updateDuration / 500;
var cell = svg.append("g")
.attr("class", "cells")
.attr("transform", "translate(" + offset + "," + (offset + 30) + ")")
.selectAll("rect");
var label = svg.append("text")
.attr("class", "label");
function update(n1) {
var n0 = cell.size();
cell = cell
.data(d3.range(n1));
cell.exit().transition()
.delay(function(d, i) { return (n0 - i) * updateDelay; })
.duration(updateDuration)
.attr("width", 0)
.remove();
cell.enter().append("rect")
.attr("width", 0)
.attr("height", cellSize)
.attr("x", function(i) {
var x0 = Math.floor(i / 100) % 10, x1 = Math.floor(i % 10);
return groupSpacing * x0 + (cellSpacing + cellSize) * (x1 + x0 * 10);
})
.attr("y", function(i) {
var y0 = Math.floor(i / 1000), y1 = Math.floor(i % 100 / 10);
return groupSpacing * y0 + (cellSpacing + cellSize) * (y1 + y0 * 10);
})
.transition()
.delay(function(d, i) { return (i - n0) * updateDelay; })
.duration(updateDuration)
.attr("width", cellSize);
label
.attr("x", offset + groupSpacing)
.attr("y", offset + groupSpacing)
.attr("dy", ".71em")
.transition()
.duration(Math.abs(n1 - n0) * updateDelay + updateDuration / 2)
.ease("linear")
.tween("text", function() {
var i = d3.interpolateNumber(n0, n1);
return function(t) {
this.textContent = formatNumber(Math.round(i(t)));
};
});
}
(function interval() {
update(Math.floor(Math.random() * 100 * 100));
setTimeout(interval, updateDelay * 100 * 100 + updateDuration + 1000);
})();
d3.select(self.frameElement).style("height", height + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment