block by mbostock afecf1ce04644ad9036ca146d2084895

Blocks Graph

Full Screen

This shows the force-directed network of mentions in the READMEs of various bl.ocks. I cleaned Micah Stubb’s data from May 2016 slightly: I removed missing nodes, solitary nodes, self-links, and redundant links. The original dataset contains 2,101 nodes and 8,617 links, and is 1.2 MB. The cleaned dataset contains 1,238 nodes and 2,602 links, and is 281 KB.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<a target="_blank" style="outline:none;"><canvas width="960" height="960"></canvas></a>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var canvas = document.querySelector("canvas"),
    context = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height,
    searchRadius = 40;

var color = d3.scaleOrdinal()
    .range(d3.schemeCategory20);

var simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody().strength(-18))
    .force("link", d3.forceLink().iterations(4).id(function(d) { return d.id; }))
    .force("x", d3.forceX())
    .force("y", d3.forceY());

d3.json("graph.json", function(error, graph) {
  if (error) throw error;

  var users = d3.nest()
      .key(function(d) { return d.user; })
      .entries(graph.nodes)
      .sort(function(a, b) { return b.values.length - a.values.length; });

  color.domain(users.map(function(d) { return d.key; }));

  simulation
      .nodes(graph.nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(graph.links);

  d3.select(canvas)
      .on("mousemove", mousemoved)
      .call(d3.drag()
          .container(canvas)
          .subject(dragsubject)
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

  function ticked() {
    context.clearRect(0, 0, width, height);
    context.save();
    context.translate(width / 2, height / 2);

    context.beginPath();
    graph.links.forEach(drawLink);
    context.strokeStyle = "#aaa";
    context.stroke();

    users.forEach(function(user) {
      context.beginPath();
      user.values.forEach(drawNode);
      context.fillStyle = color(user.key);
      context.fill();
    });

    context.restore();
  }

  function dragsubject() {
    return simulation.find(d3.event.x - width / 2, d3.event.y - height / 2, searchRadius);
  }

  function mousemoved() {
    var a = this.parentNode, m = d3.mouse(this), d = simulation.find(m[0] - width / 2, m[1] - height / 2, searchRadius);
    if (!d) return a.removeAttribute("href"), a.removeAttribute("title");
    a.setAttribute("href", "//bl.ocks.org/" + (d.user ? d.user + "/" : "") + d.id);
    a.setAttribute("title", d.id + (d.user ? " by " + d.user : "") + (d.description ? "\n" + d.description : ""));
  }
});

function dragstarted() {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d3.event.subject.fx = d3.event.subject.x;
  d3.event.subject.fy = d3.event.subject.y;
}

function dragged() {
  d3.event.subject.fx = d3.event.x;
  d3.event.subject.fy = d3.event.y;
}

function dragended() {
  if (!d3.event.active) simulation.alphaTarget(0);
  d3.event.subject.fx = null;
  d3.event.subject.fy = null;
}

function drawLink(d) {
  context.moveTo(d.source.x, d.source.y);
  context.lineTo(d.target.x, d.target.y);
}

function drawNode(d) {
  context.moveTo(d.x + 3, d.y);
  context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}

</script>