3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[d3.js(v4)]時間を軸にとった場合のラベルのformat(表示期間の変更に対応できるようにする)

Last updated at Posted at 2016-11-30

#はじまり
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);
}
3
6
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
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?