【D3.js】チャートの軸と目盛りをカスタマイズする5の方法
シンプルでスタティック(静的)なチャートであっても、細かく要件が発生しがちなのが軸(axis)と目盛り(ticks)です。
ここではD3を使って軸や目盛りを描画する際に使えるテクニックを紹介します。
それぞれ気持ち程度の解説を入れていますが、詳しくはexampleリンク先のサンプルコードを参照してください。
レスポンシブ
一番要望が多いというか、最近では基本的な要件になりつつある軸を含めたレスポンシブ対応です。
ここでは、単にリサイズするだけでなく、横幅が特定のブレイクポイント以下になった場合に目盛りの表示数なども変更しています。
axisを出力するエレメントに対して、仮のデータをバインドすることでd3の差分管理機能をaxisに対しても使えるようにしています。
1 2 3 4 |
yAxisUpdate = axis.selectAll(".yAxis").data([null]); //仮のデータをバインディング const yAxisEnter = yAxisUpdate.enter().append("g").classed("yAxis", true); //新たにaxisを追加する際の処理 yAxisUpdate.merge(yAxisEnter).call( d3.axisLeft().scale(yScale).ticks(yTicks) ); //属性値のアップデート |
目盛りのカスタマイズ
例えば、「一番最初の目盛りにだけ単位をつけて表示したい」や「1月の時だけ年月で表示したい」などの要件に対応する方法です。
tickFormatに渡すコールバックの中で、目盛りの表示方法を細かく指定することができます。
1 2 3 4 5 |
d3.axisLeft().scale(yScale) .tickFormat( (d,i) => { //この戻り値が目盛りとして表示されます。 return d; }); |
目盛りを内側に表示
表示領域の狭いスマートフォンでは、チャートのマージンを十分に取ることができない場合があります。
そういった場合は、目盛りを内側に表示することで余計なマージンを取らずにコンパクトなチャートを表示することができます。
axisLeft, axisRight, axisTop, axisBottomの各モジュールでラベルの表示位置を指定できます。
目盛りの改行
カテゴリのラベルなどで、どうしても改行が必要になる場合があります。
SVGには基本折り返すという機能がないため、非常に面倒ですが下記方法で一応実装することができます。
d3.axisが出力したエレメントからtext要素を一旦削除し、代わりにtspanで囲ったエレメントを追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
yAxis.selectAll(".tick text") .html(null) //元のtickラベルを削除 .attr("text-anchor", "start") .attr("dominant-baseline", "middle") .each((text, i, nodes) => { //ラベルに適用するテキストを改行コードを基準に分割 const st = text.split("n"); //ラベルを右揃えにするために、分割したテキストの中でもっとも長い値を取得「 const maxLength = d3.max(st, d => d.length ); //行間を設定 const lineHight = 0.6; //分割したテキストの数から、Y座標を求めるためのスケールを設定 const ypos = d3.scaleLinear().domain([0, st.length]).range([-st.length * lineHight, st.length * lineHight]); //分割したテキストをtspan要素で括って、もとのノードにアペンドする const tspanUpdate = d3.select(nodes[i]).selectAll("tspan").data(st); const tspanEnter = tspanUpdate.enter().append("tspan"); //tspanに改行のための情報を付加する tspanEnter .attr("dy", "1em") .attr("y", (d,i) => ypos(i) + "em") .attr("dx", "-" + (maxLength + 0.5) + "em") .attr("x", (d,i) => { const l = maxLength - d.length; return l + "em"}) .text(d => d) }); |
SVGでの改行については、こちらのサンプルを参照してください。
基軸の移動
表示するデータに0以下の値が入ってくる場合、基軸を0の位置へ表示したいという要望がよくあります。ここではtransformを使って0ベースラインを移動しています。
以下の例では三秒ごとにデータが入れ替わり、それに伴ってベースラインが移動します。
1 2 |
yAxis.select(".domain") .attr("transform", `translate(${xScale(0)}, 0)`); //ベースラインを0の位置へ移動する |
(おまけ)軸のクラスタリング
二つのチャートを並べて表示したい場合など。
ここでは、Y軸をトップとボトムに分けて表示しています。
素直に二つチャートを作って並べた方が早いかも。