LoginSignup
2
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-12-26

12月-26-2017 午後4-49-30.gif

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

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

D3

  // ダミーデータ作成
  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


<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]
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1