D3.jsでスピードメーターみたいなものを作成

今回は、スピードメーターみたいなものを作ってみようと思います。
D3.js は、4.0 です。

作成するスピードメーター

値は乱数ですが、CPUやメモリの使用量などの表示に組み合わせたら
それっぽくなるかもしれません。

See the Pen D3_SpeedMeter by book_stone (@book_stone) on CodePen.


ソースコード

主だった個所をメモしたいと思います。

// メーターの範囲を設定
var arcScale = d3.scaleLinear()
    .domain([0 , 100])
    .range([startAngleRate*90, endAngleRate*90]); // ここは度

メータのスケール設定です。
折れ線グラフの場合は、データの値をWidth や Heightのサイズにあうように
スケールの設定をしていましたが、今回はデータの値の大小によってメーターの針の角度が変わるため
[0, 100] ⇒ [円弧の開始角度, 円弧の終了角度] になるように設定しています。

var arc = d3.arc()
  .innerRadius(55)
  .outerRadius(60)
  .startAngle(startAngleRate * p/2) // ここはラジアン
  .endAngle(endAngleRate * p/2 )

円弧を作成しています。
innerRadius と outerRadius を設定することで円弧の幅や半径が調整できます。
startAngle と endAngle は、円弧の開始と終わりですがラジアンになるみたいです。

var svgDefs = svg.append('defs');
var mainGradient = svgDefs.append('linearGradient')
    .attr('id', 'mainGradient');

// グラデーションの設定(3色)
mainGradient.append('stop')
    .attr('class', 'stop-left') // green yellow
    .attr('offset', '0');
mainGradient.append('stop')
    .attr('class', 'stop-middle') // orange
    .attr('offset', '0.6');
mainGradient.append('stop')
    .attr('class', 'stop-right') // red
    .attr('offset', '1');

円弧のグラデーションの設定です。色の設定はCSSのクラスでしています。
offset で、グラデーションの間隔が調整できます。
上の例だと、オレンジは真ん中より少し赤よりからスタートしています。

// 目盛の追加
// 0 を100個の5間隔で配列を作成
var ticks = d3.range(0, 105, 5);
// グループ要素を目盛数分追加する
var ticks_g = svg.selectAll(".tick").data(ticks).enter()
  .append("g")
  .attr("class","tick")
  .attr("transform",function(d,i){ return "translate(" + width/2 + "," + height/2 + ")"; });

円弧の下に目盛を作成するためにまず目盛の数値を作成ています。
d3.range(0, 105, 5) は、[ 0, 5, 10, .... , 90, 95, 100] の配列です。

// 目盛りの線
var ticksLine = d3.line()
  .x(0) 
  .y(function(d) { return d; });

// 目盛りの追加
ticks_g.append("path")
  .attr("d", function(d){ 
    if(d % 20 == 0 ){ return ticksLine([ -50 , -45]); } // 主目盛
    else{ return ticksLine([-50 , -48]); }} // 補助目盛
  )
  .attr("stroke", "gray")
  .attr("stroke-width",2)
  .attr("stroke-linecap","round")
  .attr("transform",function(d,i){ return "rotate(" + arcScale(d) + ")"; });

目盛の配置をしています。
上で設定したグループ要素の持つデータ[ 0, 5, 10, .... , 90, 95, 100]が割り当てられます。
20 で割り切れる場合だけ、少し大きい目盛にしてます。
rotate で線を回転させることによって円弧に沿った目盛を表現しています。

// ラベル 位置は角度から(x,y)を求めて、貼り付け
ticks_g.append("text")
  .attr("x",  function(d){ return 32 * Math.sin(arcScale(d) * (Math.PI / 180)); }) 
  .attr("y" , function(d){ return -32 * Math.cos(arcScale(d) * (Math.PI / 180)) + 2; })
  .attr("text-anchor", "middle")
  .attr("fill", "black")
  .attr("font-size", "14px")
  .attr("font-family", "sans-serif")
  .text(function(d){ if(d % 20 == 0 ){ return d; }});

ラベルを配置をしています。
こちらも 20 で割り切れる場合だけ、文字を出力しています。
rotateで回転させてもよかったのですが、文字が回転すると見にくいため、
x, y の座標は、回転させるべく角度から三角関数で求めています。
度からラジアンに戻すため Math.PI / 180 をかけています。
(y座標の +2 は位置の調整のため追加しました。)

感想

これもとりあえず、動いたといった感じでしょうか。
見た目のインパクトがもう少しほしいところですね。

ひとまずでした。