Skip to content

Instantly share code, notes, and snippets.

@shimizu
Last active September 12, 2017 06:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shimizu/d7b0c145e2b837a27d9aa2bf4b8c5465 to your computer and use it in GitHub Desktop.
Save shimizu/d7b0c145e2b837a27d9aa2bf4b8c5465 to your computer and use it in GitHub Desktop.
force simulation - globe
license: mit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>force simulation - globe</title>
<style>
#graph {
width: 940px;
height: 460px;
}
</style>
</head>
<body>
<div id="btn">
<button data-fn="addENode">Eノード追加</button>
<button data-fn="deleteDLinkNode">Dノード・リンク削除</button>
<button data-fn="addACLink">ACリンク追加</button>
<button data-fn="deleteBCLink">BCリンク削除</button>
</div>
<div id="graph"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.1.1/d3.min.js"></script>
<script>
//描画エリアのサイズを取得する
var w = document.querySelector("#graph").clientWidth
var h = document.querySelector("#graph").clientHeight
//svgエレメントの初期化
var svg = d3.select("#graph")
.append("svg")
.attr("width", w)
.attr("height", h)
;
var linkLayer = svg.append("g");
var nodeLayer = svg.append("g");
//ノード、リンク、初期データ
var data = {
nodes:[
{id: 'A', x:500, y:500},
{id: 'B', x:0, y: 0},
{id: 'C', x:500, y:0},
{id: 'D', x:0, y:500},
],
links:[
{id:"AB", source: "A", target: "B"},
{id:"BC", source: "B", target: "C"},
{id:"CD", source: "C", target: "D"},
{id:"DB", source: "D", target: "B"},
]
};
//フォースシュミレーターオブジェクトを用意
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(d => d.id).distance(() => 100).strength((d,i) => (i * 0.1) ))
.velocityDecay(0.9)
.force("collide",d3.forceCollide(32))
.force('x', d3.forceX().strength(0.3).x(w/2))
.force('y', d3.forceY().strength(0.3).y(h/2))
.force('charge', d3.forceManyBody().strength((d,i) => -(i * 100)))
//ドラッグ時の処理
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
//svgエレメントを更新する
function updateElement(){
simulation.nodes(data.nodes);
simulation.force("link").links(data.links);
var link = linkLayer.selectAll(".link")
.data(data.links, d => d.id )
;
link.enter().append("line")
.attr("class", "link")
.attr("stroke", "black")
;
link.exit().remove();
var node = nodeLayer.selectAll(".node")
.data(data.nodes, d=> d.id )
;
var newNode = node.enter().append("g")
.attr("class", "node")
.call(drag)
;
node.exit().remove();
newNode.append("image")
.attr("xlink:href", "globe.png")
.attr("x", "-16px")
.attr("y", "-16px")
.attr("width", "32px")
.attr("height", "32px")
newNode.append("text")
.attr("x", "1.5em")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.text(d => d.id)
;
simulation.alpha(1).restart();
}
//フォースシュミレーターの計算結果を使って、ノードとリンクの位置を更新する
function ticked() {
svg.selectAll(".link")
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y)
;
svg.selectAll(".node")
.attr("transform", d => "translate("+[d.x, d.y]+")")
;
}
//ボタンにデータを変更する処理をセットする。
function setBtnEventListener() {
//ボタンクリック時のイベントハンドラ
var btnEventHandler = {
addENode:function(){
data.nodes.push({id:'E'});
data.links.push({id:"EA", source: "E", target: "C"});
updateElement();
},
addACLink:function(){
data.links.push({id:"AC", source: "A", target: "C"});
updateElement();
},
deleteDLinkNode:function(){
data.nodes = data.nodes.filter(function(d){
return d.id != "D";
});
data.links = data.links.filter(function(d){
return d.source.id != "D" && d.target.id != "D";
});
updateElement();
},
deleteBCLink:function(){
data.links = data.links.filter(function(d){
return !(d.source.id == "B" && d.target.id == "C");
});
updateElement();
}
};
d3.select("#btn").selectAll("button").on("click", function(){
btnEventHandler[this.dataset.fn]();
});
}
function main() {
updateElement();
simulation.on("tick", ticked);
setBtnEventListener();
}
main();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment