block by micahstubbs b35f2560f4205570b3328d1b40de0c6c

Blocks Graph VI

Full Screen

this iteration centers the network on the page for more pleasant full-screen viewing. it also converts the code to ES2015 in the Airbnb style

a fork of alexmacy‘s Blocks Graph - w/thumbnails


Original README.md

This is an attempt to add thumbnail images to Mike Bostock](twitter.com/mbostock)’s version of Micah Stubbsblock. The tooltip also utilizes some code from the tooltip on Daniel Overstreet‘s Beijing Air Quality 3.

Hovering over a node loads that block’s thumbnail in a tooltip. There are a couple issues that need fixing:

forked from mbostock‘s block: Blocks Graph

I added some CSS in the head for the tooltip and then defined the tooltip @ line 36:

var tooltip = d3.select("body")
    .append("div")
      .attr("class", "tooltip")
      .style("position", "absolute")
      .style("z-index", "10")
      .style("visibility", "hidden");

Also made a function for fetching and displaying the image @ line 126:

function loadTooltipThumb(d) {
    tooltip.select('*').remove();

    var thumbnailURL = 'https://bl.ocks.org/' + (d.user ? d.user + "/" : "") + 'raw/' + d.id + '/thumbnail.png';

    var top = d3.event.pageY - 150;

    tooltip.style("top", top + "px")
        .style("left", d3.event.pageX + 40 + "px")
        .style("visibility", "visible")
        .append('img')
        .attr('src', thumbnailURL)
}

…which gets called in the ‘mousemoved’ event @ line 98

There’s also something in the logic at line 93 and shrunk the searchRadius @ line 26 to hide the tooltip when not hovering over a node.

index.html

<!DOCTYPE html>
<meta charset='utf-8'>
<style>
  .tooltip {
    width: 230px;
    height: 120px;
    background-color: #ffffff;
    padding: 3px 12px;
    font-family: sans-serif;
    border: 1px solid #bbbbbb;
    box-shadow: 1px 1px 4px #bbbbbb;
	}
  img {
    max-width: 230px;
    max-height: 120px;
  }
  #canvas-container {
   width: 100%;
   text-align:center;
}

canvas {
   display: inline;
}
</style>
<a target='_blank' style='outline:none;'>
  <div id='canvas-container'>
    <canvas width='960' height='960'>Your browser does not support canvas</canvas>
  </div>
</a>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.js'></script>
<script src='vis.js'></script>

vis.js

const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const searchRadius = 10;

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

const simulation = d3
  .forceSimulation()
  .force('charge', d3.forceManyBody().strength(-18))
  .force('link', d3.forceLink().iterations(4).id(d => d.id))
  .force('x', d3.forceX())
  .force('y', d3.forceY());

const tooltip = d3
  .select('body')
  .append('div')
  .attr('class', 'tooltip')
  .style('position', 'absolute')
  .style('z-index', '10')
  .style('visibility', 'hidden');

d3.json('graph.json', (error, graph) => {
  if (error) throw error;

  const users = d3
    .nest()
    .key(d => d.user)
    .entries(graph.nodes)
    .sort((a, b) => b.values.length - a.values.length);

  color.domain(users.map(d => 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(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() {
    const a = this.parentNode;
    const m = d3.mouse(this);
    const d = simulation.find(
      m[0] - width / 2,
      m[1] - height / 2,
      searchRadius
    );

    if (!d) return a.removeAttribute('href');
    a.removeAttribute('title');
    tooltip.style('visibility', 'hidden');

    a.setAttribute(
      'href',
      `http://bl.ocks.org/${d.user ? `${d.user}/` : ''}${d.id}`
    );
    a.setAttribute(
      'title',
      `${d.id}${d.user ? ` by ${d.user}` : ''}${d.description ? `\n${d.description}` : ''}`
    );
    loadTooltipThumb(d);
  }
});

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);
}

function loadTooltipThumb(d) {
  tooltip.select('*').remove();

  const thumbnailURL = `https://bl.ocks.org/${d.user ? `${d.user}/` : ''}raw/${d.id}/thumbnail.png`;

  const top = d3.event.pageY - 150;

  tooltip
    .style('top', `${top}px`)
    .style('left', `${d3.event.pageX + 40}px`)
    .style('visibility', 'visible')
    .append('img')
    .attr('src', thumbnailURL);
}