block by HarryStevens 27a440b621960899f4584034919aa9f7

Random Beeswarm

Full Screen

From Mike Bostock’s beeswarm block:

A static beeswarm plot implemented using d3-force’s collision constraint. A Voronoi overlay improves hover interaction.

index.html

<html>
  <head>
    <style>
    body {
      margin: 0;
    }
    .cells path {
      fill: none;
      pointer-events: all;
    }
    </style>
  </head>
  <body>

    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://unpkg.com/d3-marcon@2.0.2/build/d3-marcon.min.js"></script>
    <script>
    var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNLOPQRSTUVWXYZ".split("");

    var m = d3.marcon().top(30).right(30).bottom(30).left(30).width(window.innerWidth).height(window.innerHeight);
    m.render()
    var width = m.innerWidth(), height = m.innerHeight(), svg = m.svg(), margin = {top: m.top(), left: m.left(), right: m.right()};

    var axisScale = d3.scaleLinear()
        .rangeRound([0, width])
        .domain([-100, 100]);

    var sizeScale = d3.scaleLinear()
        .range([5, 50]);

    function redraw(data) {

      sizeScale.domain(d3.extent(data, function(d) { return d.size; }));

      var simulation = d3.forceSimulation(data)
          .force("x", d3.forceX(function(d) { return axisScale(d.change); }).strength(1))
          .force("y", d3.forceY(height / 2))
          .force("collide", d3.forceCollide(function(d, i){ return sizeScale(d.size) + 1; }))
          .stop();

      for (var i = 0; i < 250; ++i) simulation.tick();

      svg.append("g")
          .attr("class", "axis")
          .attr("transform", "translate(0," + (height) + ")")
          .call(d3.axisBottom(axisScale).ticks(20));

      // JOIN
      var cell = svg.append("g")
          .attr("class", "cells")
          .selectAll("g").data(d3.voronoi()
            .extent([[0, 0], [width, height]])
            .x(function(d) { return d.x; })
            .y(function(d) { return d.y; })
            .polygons(data)).enter().append("g")
          .on("mouseover", function(d){ d3.select(this).attr("fill", "red"); })
          .on("mouseout", function(){ d3.select(this).attr("fill", "black"); });

      // voronoi
      var voronoi = cell.append("path")
          .attr("d", function(d) { return "M" + d.join("L") + "Z"; });

      // circle
      cell.append("circle")
          .attr("r", function(d) { return sizeScale(d.data.size); })
          .attr("cx", function(d) { return d.data.x; })
          .attr("cy", function(d) { return d.data.y; })

    }
    redraw(randomizeData());

    /*FUNCTIONS*/
    function randomizeData(){
      return shuffle(alphabet).map(function(d){ return {name: d, change: random(-100, 100), size: random(1, 100)} });
    }

    function random(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function shuffle(array) {
      var m = array.length, t, i;

      // While there remain elements to shuffle…
      while (m) {

        // Pick a remaining element…
        i = Math.floor(Math.random() * m--);

        // And swap it with the current element.
        t = array[m];
        array[m] = array[i];
        array[i] = t;
      }

      return array;
    }

    </script>

  </body>
</html>