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.selectやd3.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種類を設定できる。
listenerにはイベント時に呼び出す関数を登録する。listenerをnullにすると、登録していたイベントが解除される。 |
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 |
start、zoom、endのいずれかが設定される。 |
d3.event.transform |
現在のズーム倍率と移動量が設定される。 |
d3.event.sourceEvent |
mousemoveやtouchmoveなどの元の入力イベントが設定される。 |
イベントリスナーはd3.event.transformを使用して設定する。d3.event.transformをattr関数で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値として設定すれば初期状態に戻せる。
まとめ
マウス操作だけでなく、タッチイベント、ピンチイン、ピンチアウトにも対応している。