【D3.js】ノードをドラッグできる静的なForceレイアウト
アニメーションForceレイアウト
D3.jsのForceレイアウトは、tickイベント内でノードとリンクの描画をアップデートすることで、簡単にアニメーションが行えます。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
var nodes = [ {lable:"hello"}, {lable:"text"}, {lable:"hoge"} ] var links = [ { source : 0, target : 1 }, { source : 0, target : 2 }, { source : 1, target : 2 }, ] var svg = d3.select('svg'); var w = document.querySelector("body").clientWidth; var h = document.querySelector("body").clientHeight; // Force Layoutを設定 var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .gravity(0.1) .charge(-30) .friction(0.95) .linkDistance(220) .linkStrength(1); //force レイアウトの計算を開始 force.start(); var line = svg.selectAll("line") .data(links) .enter() .append("line") .attr({ "stroke": "black", "x1":function(d){ return d.source.x }, "y1":function(d){ return d.source.y }, "x2":function(d){ return d.target.x }, "y2":function(d){ return d.target.y }, }); var node = svg.selectAll("circle") .data(nodes) .enter() .append("circle") .attr({ "r":8, "fill": "black", "cx": function(d){ return d.x }, "cy": function(d){ return d.y } }) .call(force.drag); ; //update force.on("tick", function() { line .attr("x1", function(d) { return d.source.x; }) // ソースとターゲットの要素座標を指定していく .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node .attr("cx", function(d) { return d.x; }) // ノードの座標を指定していく .attr("cy", function(d) { return d.y; }); }); |
スタティックForceレイアウト
しかし用途によっては、アニメーションを行わず静的なグラフとして表示したい場合があります。
その場合はtickイベントを使わず、forceオブジェクトのtickメソッドを実行することで描画ステップを進め、好みのところでstopメソッドを実行しアニメーションを止めることで静的なグラフとして表示できます。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
var nodes = [ {lable:"hello"}, {lable:"text"}, {lable:"hoge"} ] var links = [ { source : 0, target : 1 }, { source : 0, target : 2 }, { source : 1, target : 2 }, ] var svg = d3.select('svg'); var w = document.querySelector("body").clientWidth; var h = document.querySelector("body").clientHeight; // Force Layoutを設定 var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .gravity(0.1) .charge(-30) .friction(0.95) .linkDistance(220) .linkStrength(1); force.start(); //force レイアウトの計算を開始 for (var i = 10000; i > 0; --i) force.tick(); //ワンステップ進める force.stop(); //force レイアウトの計算を終了 var line = svg.selectAll("line") .data(links) .enter() .append("line") .attr({ "stroke": "black", "x1":function(d){ return d.source.x }, "y1":function(d){ return d.source.y }, "x2":function(d){ return d.target.x }, "y2":function(d){ return d.target.y }, }); var node = svg.selectAll("circle") .data(nodes) .enter() .append("circle") .attr({ "r":8, "fill": "black", "cx": function(d){ return d.x }, "cy": function(d){ return d.y } }) .call(force.drag); ; |
スタティックForceレイアウト(ドラッグ可能)
しかし、この場合アニメーションが停止しているため各ノードをドラッグしても動きません。
ノードを静止させたままドラッグに対応させるには、ノード要素に束縛されたデータのfixedプロパティの値をtrueに設定します。
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
var nodes = [ {lable:"hello"}, {lable:"text"}, {lable:"hoge"} ] var links = [ { source : 0, target : 1 }, { source : 0, target : 2 }, { source : 1, target : 2 }, ] var svg = d3.select('svg'); var w = document.querySelector("body").clientWidth; var h = document.querySelector("body").clientHeight; // Force Layoutを設定 var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .gravity(0.1) .charge(-30) .friction(0.95) .linkDistance(220) .linkStrength(1); //force レイアウトの計算を開始 force.start(); for (var i = 10000; i > 0; --i) force.tick(); //ワンステップ進める var line = svg.selectAll("line") .data(links) .enter() .append("line") .attr({ "stroke": "black", "x1":function(d){ return d.source.x }, "y1":function(d){ return d.source.y }, "x2":function(d){ return d.target.x }, "y2":function(d){ return d.target.y }, }); var node = svg.selectAll("circle") .data(nodes) .enter() .append("circle") .attr({ class:"node", "r":8, "fill": "black", "cx": function(d){ return d.x }, "cy": function(d){ return d.y } }) .call(force.drag); ; d3.selectAll(".node").each(function(d){ d.fixed = true }) //update force.on("tick", function() { line .attr("x1", function(d) { return d.source.x; }) // ソースとターゲットの要素座標を指定していく .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node .attr("cx", function(d) { return d.x; }) // ノードの座標を指定していく .attr("cy", function(d) { return d.y; }); }); |