#はじまり
d3.scaleTime
を使うと、zoomしようが何しようが期間の幅にあわせていいかんじに表示してくれます。
とはいえ、デフォルトのままでは日本人としては微妙に読みづらいですし、
[Fri 02,Sat 03,Dec 04,Mon 05]
のように曜日と月名が入り混じって不可解極まる表示になったりします。
ここらへんの仕組みと解消方法について。
#デモ
デフォルトの表示(jsfiddle)
デフォルトと同じように動くformat関数(jsfiddle)
日本語化(jsfiddle)
曜日アリと曜日ナシが入り混じらないように日付の表示を調整(jsfiddle)
#問題の説明
曜日アリと曜日ナシが入り交じる理由について説明します。
下記のコードのtickFormat
のあたりにずらずらと条件文がならんでいますが、
これがformatの切り替えのロジックです。
単純に目盛りとして渡された日付がどこで切り捨てられるかを見て、formatを切り替えています。
だいたいこれで上手くいくんですが、「日曜日の0時0分0秒」のように月毎/週毎/日毎のどれでもありうるような値がくると表記が混乱しちゃうんですね。
これを解消するには、期間の幅や目盛りの幅なども含めて判定する必要があります。
あと日本語化についてですが、formatMillisecond(date);
は単純に文字列を返してるだけなので、
momentでもd3.timeFormatでもなんでもいいのでテキトーに文字列を返してあげれば日本語化します。
chartSvg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + yAxisWidth + ",0)");
var yScale = d3.scaleTime()
.range([padding, height - padding])
.domain([startDate,endDate]);
function draw() {
var yAxis = d3.axisLeft(yScale)
.tickFormat(function(date){
if (d3.timeSecond(date) < date){
return formatMillisecond(date);
} else if (d3.timeMinute(date) < date ){
return formatSecond(date);
} else if (d3.timeHour(date) < date){
return formatMinute(date);
} else if (d3.timeDay(date) < date){
return formatHour(date);
} else if (d3.timeMonth(date) < date){
//↓この判定で「週ごとに刻んで日曜日」なのか「一日毎に刻んで日曜日」か判別できない。
if (d3.timeWeek(date) < date){
return formatDay(date);
} else {
return formatWeek(date);
}
} else if (d3.timeYear(date) < date){
return formatMonth(date);
} else {
return formatYear(date);
}
});
chartSvg.selectAll(".y-axis")
.call(yAxis);
}
draw();
#解決方法
下記のコードは、数日~数年あたりの期間を表示するために調整したものです。
d3.jsのscaleオブジェクトから.ticks(n)
を呼ぶと、目盛りのリストが取得できます。
その幅を見て表示を調整しています。
function draw() {
var ticks = yScale.ticks(10);//default値が10であるから
var tickDiff = moment(ticks[1]).diff(ticks[0]);
//目盛り幅が1~3日分だったら日毎に刻んでると判定する
var isDayInterval = (86400000 === tickDiff || 86400000 * 2 === tickDiff || 86400000 * 3 === tickDiff);
var yAxis = d3.axisLeft(yScale)
.tickFormat(function(date){
var m = moment(date);
if (d3.timeDay(date) < date){
if (date.getHours() === 0){
return m.format('D (ddd)');
} else {
return m.format('A hh');
}
} else if (d3.timeMonth(date) < date){
if (d3.timeWeek(date) < date || isDayInterval){
return m.format('D (ddd)');
} else {
return m.format('M月D日');
}
} else if (d3.timeYear(date) < date){
if (isDayInterval){
return m.format('M/D (ddd)');
} else {
return m.format('M月');
}
} else {
return m.format('YYYY');
}
});
chartSvg.selectAll(".y-axis")
.call(yAxis);
}