Built with blockbuilder.org
Last active
September 12, 2017 06:03
-
-
Save shimizu/d7b0c145e2b837a27d9aa2bf4b8c5465 to your computer and use it in GitHub Desktop.
force simulation - globe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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