block by harrystevens 3bf92aec92634fb175a7050b7cb60f82

Bar Update Pattern on Canvas

Full Screen

See also: Bar Update Pattern.

TODO: Figure out how to apply the margin convention on Canvas.

index.html

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      margin: 0;
    }
  </style>
</head>
<body>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://unpkg.com/jeezy@1.12.0/lib/jeezy.min.js"></script>
  <script>
    var alpha = "abcdefg".split("");

    var min = 0, max = 10;

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

    var canvas = d3.select("body").append("canvas")
        .attr("width", width)
        .attr("height", height);

    var context = canvas.node().getContext("2d");
    var customBase = document.createElement("custom");
    var custom = d3.select(customBase);

    var x = d3.scaleBand()
      .rangeRound([0, width])
      .domain(alpha)
      .padding(.2);

    var y = d3.scaleLinear()
      .range([height, 0])
      .domain([min, max]);

    var color = d3.scaleOrdinal(["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f","#e5c494"]);

    bind(random_data());
    draw();
    
    d3.interval(function(){
      bind(random_data());
      var t = d3.timer(function(elapsed) {
        draw();
        if (elapsed > 750) t.stop();
      });
    }, 1000);

    function bind(data){

      var x_var = Object.keys(data[0])[0], y_var = Object.keys(data[0])[1];
      
      // join
      var bar = custom.selectAll("custom.bar")
        .data(data, function(d){ return d[x_var]; });

      var amount = custom.selectAll("custom.amount")
        .data(data, function(d){ return d[x_var]; });

      // update
      bar
        .transition()
          .attr("y", function(d){ return y(d[y_var]); })
          .attr("height", function(d){ return height - y(d[y_var]); });

      amount
        .transition()
          .attr("y", function(d){ return y(d[y_var]); })
          .text(function(d){ return d[y_var]; });

      // enter
      bar.enter().append("custom")
          .attr("class", "bar")
          .attr("x", function(d){ return x(d[x_var]); })
          .attr("y", function(d){ return y(d[y_var]); })
          .attr("width", x.bandwidth())
          .attr("height", function(d){ return height - y(d[y_var]); })
          .attr("fill", function(d){ return color(d[x_var]); });

      amount.enter().append("custom")
          .attr("class", "amount")
          .attr("x", function(d){ return x(d[x_var]) + x.bandwidth() / 2; })
          .attr("y", function(d){ return y(d[y_var]); })
          .attr("dy", 16)
          .text(function(d){ return d[y_var]; });

    }

    function draw(){

      context.fillStyle = "#fff";
      context.fillRect(0, 0, width, height);

      var bars = custom.selectAll("custom.bar");

      bars.each(function(d, i){

        var node = d3.select(this);
        context.fillStyle = node.attr("fill");
        context.fillRect(node.attr("x"), node.attr("y"), node.attr("width"), node.attr("height"))

      });

      var amounts = custom.selectAll("custom.amount");

      amounts.each(function(d, i){
        
        var node = d3.select(this);
        context.font = "16px Helvetica Neue";
        context.fillStyle = "#fff";
        var x = +node.attr("x");
        var y = +node.attr("y");
        var dy = +node.attr("dy");
        context.fillText(node.text(), x, y + dy);
      });
    }

    function random_data(){
      return alpha.map(function(d, i){
        return {
          name: d,
          value: jz.num.randBetween(min + 1, max)
        }
      });
    }

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