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]