d3 v4でGithubのcontributionヒートマップっぽいの作ってみた

![12月-26-2017 午後4-49-30.gif](https://qiita-image-store.s3.amazonaws.com/0/209160/8dc54e2c-d395-03d9-ee39-27efe358e3ca.gif)

D3を業務で使うので勉強がてらGithubのContributionヒートマップっぽいの作ってみた。
ソースコードは雑だが、見た目それっぽいのはできた。

D3は一年以上前に、v3→v4にメジャーアップデートしたのだけど、結構、v4のドキュメントがなくて辛かった。

## D3
```javascript
  // ダミーデータ作成
  var days = 365
  var data = [];
  for (var i = 0; i < days; i++) {
    data.push(Math.floor(Math.random() * 30));
  }
  var svg = d3.select("svg"),
    margin = {
      top: 40,
      right: 30,
      bottom: 20,
      left: 30
    },
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // データのindexから第何週目かを取得
  var getWeekIndex = function(index) {
    return Math.floor(index / 7);
  }
  // データのindexから曜日index(0-6)を取得
  var getDayIndex = function(index) {
    return index % 7;
  }
  console.log(data);
  var opacity = d3.scaleLinear()
    .domain([0, 30])
    .range([0, 1]);

  var padding = 1;
  var rectSize = height / 7;

  // tooltipsのdiv作成
  var div = d3.select("body")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

  // 各日付のrectを生成
  var rect = g.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("width", rectSize)
    .attr("height", rectSize)
    .attr("x", function(d, i) {
      return getWeekIndex(i) * rectSize;
    })
    .attr("y", function(d, i) {
      return getDayIndex(i) * rectSize;
    })
    .style("fill", "green")
    .style("opacity", function(d) {
      return opacity(d)
    })
    .on("mouseover", function(d) {
      div.transition()
        .duration(500)
        .style("opacity", 0);
      div.transition()
        .duration(200)
        .style("opacity", .7);
      div.html("<b>" + d + " contributions</b><br><span> on Jan 1, 2018</span>")
        .style("left", d3.event.pageX - 50 + "px")
        .style("top", d3.event.pageY - 50 + "px");
    })


  // 軸を生成
  var monthsLabel = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  var weekDayLabel = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];

  var xScale = d3.scaleLinear()
    .domain([0, 11])
    .range([0, rectSize * 52]);

  var yScale = d3.scaleLinear()
    .domain([0, 14])
    .range([-height, 0]);

  var xAxis = d3.axisBottom(xScale)
    .ticks(monthsLabel.length)
    .tickFormat(function(d) {
      return monthsLabel[d];
    })

  var yAxis = d3.axisLeft(yScale)
    .ticks(weekDayLabel.length * 2)
    .tickFormat(function(d, i) {
      if (i % 2 == 0) {
        return '';
      } else {
        return weekDayLabel[(i - 1) / 2];
      }
    })

  g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0, " + height + ")")
    .call(xAxis);

  g.append("g")
    .attr("class", "axis axis--y")
    .attr("transform", "translate(0, " + height + ")")
    .call(yAxis);
```
## HTML CSS
```html

<style>
  form {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    position: absolute;
    left: 10px;
    top: 10px;
  }

  label {
    display: block;
  }

  .axis path,
  .axis line {
    display: none;
  }

  div.tooltip {
    position: absolute;
    text-align: center;
    color: #FFFFFF;
    padding: 5px;
    background: #000000;
    border-radius: 3px;
    font-size: 10px;
  }

  div.tooltip span {
    color: #DDDDDD;
  }
</style>

<svg width="1400" height="170"></svg>
```
ダミーデータは大きさ365の配列で、0~30の値がランダムに入っている。（今回はdateデータは入れてない。）

```
[4, 7, 23, 10, 5, 27, 1, 24, 8, 2, ... , 6, 16, 12, 28, 28, 10]
```