Githubの芝っぽいものを作ってみたくなったので下記をd3.js v4で動くようにしてみた。
Qiitaの投稿アクティビティをGitHubのように表示するヤツ
やったこと
公式ドキュメントのChanges in D3 4.0の通りにしただけ。
https://github.com/d3/d3/blob/master/CHANGES.md
デモ
ソース
$(function(){
/*
フォームの入力内容を元にAPIに問い合わせ
*/
$("#form").on("submit", function(e) {
$("#btn-submit").prop("disabled", true);
var user_id = $("#user_id").val()
$.ajax({
type: "GET",
url: "https://qiita.com/api/v2/users/" + user_id + "/items?page=1&per_page=100",
success: onSucceed
});
e.preventDefault();
});
// 日付のフォーマット
var format = d3.timeFormat("%Y-%m-%d");
/*
APIから受け取った情報を元にカレンダー描画
*/
function onSucceed(data) {
var item_dates = {};
$.each(data, function(idx, val) {
var created_at = format(d3.timeDay.floor(new Date(val['created_at'])));
if (created_at in item_dates)
item_dates[created_at]++;
else
item_dates[created_at] = 1;
});
drawCalendar(item_dates);
$("#btn-submit").prop("disabled", false);
}
/*
カレンダーの描画
*/
var drawCalendar = (function() {
// セルの1辺のサイズ
var CELL_SIZE = 15;
// ラベルの高さ
var LABEL_HEIGHT = 11;
// カレンダ表示のマージン
var MARGIN_LEFT = 25;
var MARGIN_TOP = 15;
// 表示する日付の範囲(過去1年間)
var rangeBegin = d3.timeDay.offset(new Date, -365);
var rangeEnd = new Date;
var dateRange = d3.timeDays(rangeBegin, rangeEnd);
var monthRange = d3.timeMonths(rangeBegin, rangeEnd);
// 矩形のX座標のオフセット値を算出する関数
var offsetX = (function() {
var firstYearOffset = d3.timeWeek.count(d3.timeYear(rangeBegin), rangeBegin) * -1;
var bounderyDate = d3.timeYears(rangeBegin, rangeEnd)[0];
var lastDayOfFirstYear = d3.timeDay.offset(bounderyDate, -1);
var lastWeekOfFirstYear = d3.timeWeek.count(d3.timeYear(lastDayOfFirstYear), lastDayOfFirstYear);
var lastYearOffset = d3.timeWeek.count(d3.timeYear(lastDayOfFirstYear), lastDayOfFirstYear) + firstYearOffset;
return function(sourceDate) {
if (sourceDate.getFullYear() == rangeBegin.getFullYear())
return firstYearOffset;
return lastYearOffset;
}
})();
// メイン処理:カレンダー描画
return function(originalDataset) {
// Objectをkeyでフィルタする関数
function objectFilter(obj, predicate) {
var result = {}, key;
for (key in obj) {
if (obj.hasOwnProperty(key) && predicate(key, obj[key])) {
result[key] = obj[key];
}
}
return result;
}
var dataset = objectFilter(originalDataset, function(k, v) {
var aDay = new Date(k);
return rangeBegin <= aDay && aDay <= d3.timeDay.offset(rangeEnd, 1);
});
// 件数を3段階に分類するスケール関数
var countScale = d3.scaleLinear()
.domain([1, d3.max(d3.values(dataset))])
.rangeRound([1, 3]) // 3段階で色分け
.clamp(true);
// 段階ごとに色分け
// Thanks to colorbrew (http://colorbrewer2.org/)
var colorScale = d3.scaleOrdinal()
.domain([1, 2, 3])
.range(["#f7fcb9","#addd8e","#31a354"]);
var color = function(f) { return colorScale(countScale(f)); }
// svg要素を選択
var svg = d3.select(".weed");
svg.selectAll("*").remove();
svg.append("g");
// 日毎の矩形を生成
var rect = svg.selectAll(".day")
.data(dateRange)
.enter()
.append("rect")
.attr("class", "day")
.attr("width", CELL_SIZE - 1)
.attr("height", CELL_SIZE - 1)
.attr("x", function(d){ return (d3.timeWeek.count(d3.timeYear(d), d) + offsetX(d)) * CELL_SIZE + MARGIN_LEFT; })
.attr("y", function(d){ return d.getDay() * CELL_SIZE + MARGIN_TOP; })
.attr("fill", "#e6e6e6")
.datum(format);
// 投稿のあった日のツールチップと背景色を設定
rect.filter(function(d){ return d in dataset; })
.attr("fill", function(d){ return colorScale(countScale(dataset[d])); })
.append("title")
.text(function(d){ return d + " (投稿:" + dataset[d] + "件)"; });
// 曜日のラベル
dayLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
svg.selectAll(".dayLabel")
.data(d3.range(7))
.enter()
.append("text")
.attr("class", "dayLabel")
.attr("x", 0)
.attr("y", function(d){ return d * CELL_SIZE + LABEL_HEIGHT + MARGIN_TOP; })
.attr("font-size", LABEL_HEIGHT)
.attr("fill", "gray")
.text(function(d){ return dayLabels[d]; });
// 月のラベル
monthLabels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
svg.selectAll(".monthLabel")
.data(monthRange)
.enter()
.append("text")
.attr("class", "monthLabel")
.attr("x", function(d){ return (d3.timeWeek.count(d3.timeYear(d), d) + offsetX(d)) * CELL_SIZE + MARGIN_LEFT; })
.attr("y", LABEL_HEIGHT)
.attr("font-size", LABEL_HEIGHT)
.attr("fill", "gray")
.text(function(d){ return monthLabels[d.getMonth()]; });
}
})(); // drawCalendar
});
差分