block by harrystevens bc938c8d45008d99faed47039fbe5d49

Force Update Pattern

Full Screen

An attempt to replicate Mike’s General Update Pattern, III with a force layout. The update has four parts: (1) Join, (2) Exit, (3) Update, (4) Enter.

See also: Voronoi Update Pattern II, Voronoi Update Pattern, and Modifying a Force Layout

index.html

<html>
  <head>
  </head>
  <body>

    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://unpkg.com/jeezy@1.11.2/lib/jeezy.min.js"></script>
    <script>

    var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");

    var width = window.innerWidth,
        height = window.innerHeight;

    var nodes = randomizeData();

    var simulation = d3.forceSimulation(nodes)
        .force("charge", d3.forceManyBody().strength(-150))
        .force("forceX", d3.forceX().strength(.1))
        .force("forceY", d3.forceY().strength(.1))
        .force("center", d3.forceCenter())
        .alphaTarget(1)
        .on("tick", ticked);

    var svg = d3.select("body").append("svg").attr("width", width).attr("height", height)
        g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"),
        node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");

    d3.interval(function(){
      restart(randomizeData())
    }, 2000);

    function restart(nodes) {

      // transition
      var t = d3.transition()
          .duration(750);

      // Apply the general update pattern to the nodes.
      node = node.data(nodes, function(d) { return d.name;});

      node.exit()
          .style("fill", "#b26745")
        .transition(t)
          .attr("r", 1e-6)
          .remove();

      node
          .transition(t)
            .style("fill", "#3a403d")
            .attr("r", function(d){ return d.size; });

      node = node.enter().append("circle")
          .style("fill", "#45b29d")
          .attr("r", function(d){ return d.size })
          .merge(node);

      // Update and restart the simulation.
      simulation.nodes(nodes)
        .force("collide", d3.forceCollide().strength(1).radius(function(d){ return d.size + 10; }).iterations(1));

    }

    function ticked() {
      node.attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; })

    }

    function randomizeData(){
      var d0 = jz.arr.shuffle(alphabet),
        d1 = [],
        d2 = [];
      for (var i = 0; i < jz.num.randBetween(1, alphabet.length); i++){
        d1.push(d0[i]);
      }
      d1.forEach(function(d){
        d2.push({name: d, size: jz.num.randBetween(0, 50)})
      });
      return d2;
    }

    </script>
  </body>
</html>