D3.js zoomの使い方

D3のマウス・タッチイベントに対応するzoomの使い方を紹介する。

サンプルプログラム

グラフでドラッグ(Drag)・スワイプ(Swipe)、マウスホイール(Mouse wheel)・ピンチイン(Pinch in)を試す。下のリセットボタンをクリックすると初期位置に戻る。

コードを確認

サンプルコード

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>D3 v5 zoom</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>

  <script>
    var width = 800;
    var height = 600; // グラフの高さ

    var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

    var dataset = [];
    for (var i = 0; i < 300; i++) {
      var r = 0.5 * d3.min([height, width]) * Math.random();
      var t = 2 * Math.PI * Math.random();
      dataset.push({
        "x": width / 2 + r * Math.cos(t),
        "y": height / 2 + r * Math.sin(t)
      });
    }

    // 軸設定
    var x = d3.scaleLinear().domain([0, width]).range([0, width]);
    var y = d3.scaleLinear().domain([0, height]).range([0, height]);

    var xAxis = d3.axisBottom(x)
      .ticks(width / height * 10) //
      .tickSize(height)
      .tickPadding(8 - height);

    var yAxis = d3.axisRight(y)
      .ticks(10)
      .tickSize(width)
      .tickPadding(8 - width);

    var gX = svg.append("g").call(xAxis);
    var gY = svg.append("g").call(yAxis);

    var view = svg.selectAll("circle")
      .data(dataset)
      .enter()
      .append("circle")
      .attr("class", "view")
      .attr("cx", function (d) { return d.x })
      .attr("cy", function (d) { return d.y })
      .attr("r", 5)
      .attr("fill", "steelblue")
      .attr("stroke", "black");

    d3.select("#resetButton")
      .on("click", resetted);

    var zoom = d3.zoom()
      .scaleExtent([1, 40])
      .translateExtent([
        [-100, -100],
        [width + 90, height + 100]
      ])
      .on("zoom", zoomed);

    svg.call(zoom);

    function zoomed(event) {
      view.attr("transform", event.transform);
      gX.call(xAxis.scale(event.transform.rescaleX(x)));
      gY.call(yAxis.scale(event.transform.rescaleY(y)));
    }

    function resetted() {
      svg.transition()
        .duration(750)
        .call(zoom.transform, d3.zoomIdentity);
    }
  </script>
  <div>
    <button id='resetButton'>reset</button>
  </div>

</body>

</html>

コード説明

zoomを設定するときは、まずd3.zoom()でzoomイベントのプロトタイプを呼び出し、callメソッドでzoomイベントを登録するSVG要素を設定する。ここではSVG要素を設定しているが、d3.selectd3.selectAllで選択した要素に登録できる。

var zoom = d3.zoom()
  .scaleExtent([1, 40])
  .translateExtent([
    [-100, -100],
    [width + 90, height + 100]
  ])
  .on("zoom", zoomed);

svg.call(zoom);

d3.zoom()には次の関数が用意されている。

関数 説明
zoom.scaleExtent() 倍率の最小値と最大値を設定する。
[k0, k1]のような2つの値の配列で設定する。
既定値は[0, ∞]である。
zoom.translateExtent() 移動可能範囲を設定する。
[[x0, y0], [x1, y1]]のような2次元配列で設定する。
x0、y0は左上の座標、x1、y1は右下の座標を設定する。
既定値は[[-∞, -∞], [+∞, +∞]]である。
zoom.on(typenames, listener) イベントを登録する。typenamesには次の3種類を設定できる。
  • start - zoom開始時
  • zoom - zoom中
  • end - zoom終了時
listenerにはイベント時に呼び出す関数を登録する。listenernullにすると、登録していたイベントが解除される。

on関数はイベントリスナー、つまりイベント時に呼び出される関数を登録する。

  function zoomed() {
    view.attr("transform", d3.event.transform);
    gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
    gY.call(yAxis.scale(d3.event.transform.rescaleY(y)));
  }

zoomのイベントリスナーが呼び出されると、d3.eventは現在のzoomイベントに設定され、次のフィールドが設定される。

メソッド 説明
d3.event.target 関連するD3のzoom behaviorが設定される。
d3.event.type startzoomendのいずれかが設定される。
d3.event.transform 現在のズーム倍率と移動量が設定される。
d3.event.sourceEvent mousemovetouchmoveなどの元の入力イベントが設定される。

イベントリスナーはd3.event.transformを使用して設定する。d3.event.transformattr関数でSVG要素のtransform属性に割り当てると、現在の倍率で変換される。スケールは、transformに設定されている次の関数で変更できる。

メソッド 説明
transform.rescaleX(scale) 入力したscaleのdomain範囲を現在のzoom値(x方向)に合わせて変更する。
transform.rescaleY(scale) 入力したscaleのdomain範囲を現在のzoom値(y方向)に合わせて変更する。

最後に、リセットボタンを押したときに元の位置と倍率に戻す関数を設定する。d3.zoomIdentityには倍率0、移動量0のtransform属性が登録されている。

svg.call(zoom.transform, d3.zoomIdentity);

上のようにcallメソッドで呼び出し、現在のtransform値として設定すれば初期状態に戻せる。

まとめ

マウス操作だけでなく、タッチイベント、ピンチイン、ピンチアウトにも対応している。