d3-drag モジュールとは
D3.js は、マウスによるドラッグ操作や、タブレットなどでのタッチドラッグ操作を扱うための d3-drag
モジュールを提供しています(d3
モジュールに内包されています)。
d3-drag
は Force Simulation (d3-force
) 用のモジュールではありませんが、よく d3-force
と組み合わせて使用されます。
Force Simulation では、各ノードの座標値が tick
イベントと連動して刻々と変化するため、ドラッグ中はノードの座標値をマウスカーソル位置に固定するなどの処理が必要です。
d3-drag の基本的な使い方
次のサンプルでは、svg
内の circle
要素をドラッグして動かせるようにしています。
ドラッグ挙動オブジェクトの作成
マウスによるドラッグ(およびタブレットでのタッチドラッグ)の挙動を定義するためのオブジェクトは、d3.drag()
で生成することができます。
このオブジェクトは、公式には drag behavior と呼ばれていますが、ここでは「ドラッグ挙動オブジェクト」と呼ぶことにします。
作成したドラッグ挙動オブジェクトは、D3 セレクションオブジェクトの call()
メソッドに渡すことでアタッチできます。
次のコードでは、circle
セレクションオブジェクトにドラッグ挙動オブジェクトをアタッチしています。
上記のコードでは、circle
要素群のセレクションオブジェクトの call()
メソッドに、d3.drag()
で作成したドラッグ挙動オブジェクトを渡しています。
これは、ドラッグ挙動オブジェクトを関数として呼び出してセレクションオブジェクトを引数として渡す書き方 (dragBehavior(circles)
) と同じ意味になりますが、上記のように call()
メソッドを使うことで、メソッドチェーンに組み込む形でドラッグ挙動を設定することができます。
ドラッグ関連のイベントハンドラーの実装
下記はドラッグ開始時に呼び出されるイベント (start
) のハンドラー定義です。
ドラッグを開始したノードオブジェクトがパラメーター (d
) として渡されるので、現在の座標値を fx
および fy
プロパティにセットしています。
ノードオブジェクトのこれらのプロパティをセットすると、Force Simulation による座標値更新を一時停止し、そのノードの座標を固定するという意味になります。
また、alphaTarget
値を 0.3 に設定して(デフォルトは 0)シミュレーションを再開することで、ドラッグ中にシミュレーションが停止 (alpha=0
) してしまわないようにします。
// ドラッグ開始時のイベント
function dragStarted(event, d) {
if(!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
下記はドラッグ中に繰り返し呼び出されるイベント (drag
) のハンドラー定義です。
イベントオブジェクトから現在のマウス座標を取得し、ノードの固定座標値としてセットすることで、ノードをマウスカーソル位置に追従させます。
// ドラッグ中のイベント
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
下記はドラッグ終了時に呼び出されるイベント (end
) のハンドラー定義です。
ノードの fx
および fy
プロパティを null
にリセットすることで、Force Simulation による座標値更新を有効化します。
また、alphaTarget
値を 0 に戻し、時間経過でシミュレーションが自動的に停止するようにします(alpha
値は alphaTarget
に向かって減少していき、alphaMin
値を下回ったときに計算が停止します)。
// ドラッグ終了時のイベント
function dragEnded(event, d) {
if(!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
ドラッグ中にノードが激しく動き回ってしまう場合は、start
イベントハンドラで指定する alphaTarget
の値を小さい値(0.05 ~ 0.1 くらい)に設定すると、動きが安定しやすくなります。
ドラッグ挙動オブジェクトを生成する関数を定義する
多くの場合、ドラッグ挙動オブジェクトの設定コードは似たような実装になります。 ここでは、ドラッグ挙動オブジェクトを生成するコードを関数化してみます。
/** ドラッグ挙動オブジェクトを作成する */
function createDragBehavior(simulation) {
return d3.drag()
.on("start", function(event, d) {
if(!event.active) simulation.alphaTarget(0.3).restart()
d.fx = d.x
d.fy = d.y
}).on("drag", function(event, d) {
d.fx = event.x
d.fy = event.y
}).on("end", function(event, d) {
if(!event.active) simulation.alphaTarget(0)
d.fx = null
d.fy = null
})
}
あとは、この関数によって作成したドラッグ挙動オブジェクトを、任意の D3 セレクションオブジェクトに設定してやれば OK です。