block by bollwyvl 4f0f3a4cf3e960f0e2a9

d3.layout.grid with icons

Full Screen

Grid layout demo.

In response to this question.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  font-family: Helvetica;
  font-size: 10px;
  background-color: #333333;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
.rect {
  fill: #222;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="./d3-grid.js"></script>
<script>
var color = d3.scale.ordinal()
  .domain(["error", "nominal", "unknown"])
  .range(["#bc1919", "#428bca", "#777070"]);

var data = [].concat(
  d3.range(1).map(maker("error")),
  d3.range(1).map(maker("nominal")),
  d3.range(2).map(maker("unknown"))
);

var padding = {
    top: 50,
    right: 50,
    bottom: 50,
    left: 50
  },
  docEl = document.documentElement,
  width = docEl.clientWidth - padding.left - padding.right,
  height = docEl.clientHeight - padding.top - padding.bottom,
  iconSize = 16;

var icon;

var grid = d3.layout.grid()
  .bands()
  .size([width, height]);

var buttons = d3.select("body").selectAll("button")
  .data(color.domain())
  .enter().insert("button", ":first-child")
    .text(function(d){ return "Add " + d;})
    .on("click", function(d){
      data.push(maker(d)());
      update();
    });

var svg = d3.select("body").append("div").append("svg")
  .attr({
    width: width + padding.left + padding.right,
    height: height + padding.top + padding.bottom
  })
.append("g")
  .attr({
    transform: "translate(" + padding.left + " " + padding.top +")"
  });

d3.xml("./person.svg",  "image/svg+xml", function(error, frag) {
  var node = frag.getElementsByTagName("g")[0];
  icon = function(){
    return node.cloneNode(true);
  }
  //use plain Javascript to extract the node
  update();
});

function update(){

  data.sort(function(a, b){
    return a.state.localeCompare(b.state) ||  a.id - b.id;
  });

  grid(data);

  var dot = svg.selectAll(".icon")
    .data(data, function(d){ return d.id; });

  dot.enter().append(icon)
    .attr({
      "class": "icon",
      transform: tx_xy
    })
    .style({opacity: 1e-6})
    .each(colorize)
    .on("click", function(d, i){
      data.splice(data.indexOf(d), 1);
      update();
    });

  dot.transition()
    .delay(function(d, i){ return i / 10; })
    .attr({
      transform: tx_xy
    })
    .style({opacity: 1})
    .each(colorize);

  dot.exit().transition()
    .style({opacity: 1e-6})
    .remove();
}

function colorize(d){
  d3.select(this).selectAll("*")
    .style({fill: function(){return color(d.state);}});
}

function tx_xy(d){
  var scale = Math.min.apply(null,
    grid.nodeSize().map(function(d){ return d/iconSize; }));
  var tx = "translate(" + d.x + "," + d.y + ")" + " scale(" + scale + ") ";
  return tx;
}

function maker(state){
  maker._ID = maker._ID ? maker._ID : 1;
  return function(){
    return {state: state, id: maker._ID++};
  }
}
</script>

d3-grid.js

(function() {
  d3.layout.grid = function() {
    var mode = "equal",
        layout = _distributeEqually,
        x = d3.scale.ordinal(),
        y = d3.scale.ordinal(),
        size = [1, 1],
        actualSize = [0, 0],
        nodeSize = false,
        bands = false,
        padding = [0, 0],
        cols, rows;

    function grid(nodes) {
      return layout(nodes);
    }

    function _distributeEqually(nodes) {
      var i = -1, 
          n = nodes.length,
          _cols = cols ? cols : 0,
          _rows = rows ? rows : 0,
          col, row;

      if (_rows && !_cols) {
        _cols = Math.ceil(n / _rows)
      } else {
        _cols || (_cols = Math.ceil(Math.sqrt(n)));
        _rows || (_rows = Math.ceil(n / _cols));
      }

      if (nodeSize) {
        x.domain(d3.range(_cols)).range(d3.range(0, (size[0] + padding[0]) * _cols, size[0] + padding[0]));
        y.domain(d3.range(_rows)).range(d3.range(0, (size[1] + padding[1]) * _rows, size[1] + padding[1]));
        actualSize[0] = bands ? x(_cols - 1) + size[0] : x(_cols - 1);
        actualSize[1] = bands ? y(_rows - 1) + size[1] : y(_rows - 1);
      } else if (bands) {
        x.domain(d3.range(_cols)).rangeBands([0, size[0]], padding[0], 0);
        y.domain(d3.range(_rows)).rangeBands([0, size[1]], padding[1], 0);
        actualSize[0] = x.rangeBand();
        actualSize[1] = y.rangeBand();
      } else {
        x.domain(d3.range(_cols)).rangePoints([0, size[0]]);
        y.domain(d3.range(_rows)).rangePoints([0, size[1]]);
        actualSize[0] = x(1);
        actualSize[1] = y(1);
      }

      while (++i < n) {
        col = i % _cols;
        row = Math.floor(i / _cols);
        nodes[i].x = x(col);
        nodes[i].y = y(row);
      }

      return nodes;
    }

    grid.size = function(value) {
      if (!arguments.length) return nodeSize ? actualSize : size;
      actualSize = [0, 0];
      nodeSize = (size = value) == null;
      return grid;
    }

    grid.nodeSize = function(value) {
      if (!arguments.length) return nodeSize ? size : actualSize;
      actualSize = [0, 0];
      nodeSize = (size = value) != null;
      return grid;
    }

    grid.rows = function(value) {
      if (!arguments.length) return rows;
      rows = value;
      return grid;
    }

    grid.cols = function(value) {
      if (!arguments.length) return cols;
      cols = value;
      return grid;
    }

    grid.bands = function() {
      bands = true;
      return grid;
    }

    grid.points = function() {
      bands = false;
      return grid;
    }

    grid.padding = function(value) {
      if (!arguments.length) return padding;
      padding = value;
      return grid;
    }

    return grid;
  };
})();

person.svg

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   width="16"
   height="16">
  <g>
    <circle r="4.5" cx="8" cy="5"/>
    <path
       d="M 2.5 7.5625 C 0.63673041 7.603186 0 8.4066937 0 12.8125 C 0 15.979211 2.5625 16 2.5625 16 L 13.1875 16 C 13.1875 16 16 16.038923 16 12.8125 C 16 7.9877217 15.077376 7.5449568 12.6875 7.5625 C 11.711743 9.1321682 9.9842098 10.1875 8 10.1875 C 6.0124876 10.1875 4.2560805 9.1365289 3.28125 7.5625 C 3.0019314 7.5489166 2.7376619 7.5573105 2.5 7.5625 z "/>
  </g>
</svg>