Skip to content

Instantly share code, notes, and snippets.

@pram
Created March 17, 2017 17:05
Show Gist options
  • Save pram/5cba303e464960c00054154fb33d5742 to your computer and use it in GitHub Desktop.
Save pram/5cba303e464960c00054154fb33d5742 to your computer and use it in GitHub Desktop.
zoomable, draggable force layout with labels
license: mit
border: yes
{
"nodes": [
{
"id": 1,
"name": "A",
"icon": "\uf113",
"radius": 30,
"fill": "#565560",
"stroke": "#969696"
},
{
"id": 2,
"name": "B",
"icon": "\uf092",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 3,
"name": "C",
"icon": "\uf2a5",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 4,
"name": "D",
"icon": "\uf085",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 5,
"name": "E",
"icon": "\uf1e5",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 6,
"name": "F",
"icon": "\uf0e4",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 7,
"name": "G",
"icon": "\uf2b6",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 8,
"name": "H",
"icon": "\uf1eb",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 9,
"name": "I",
"icon": "\uf127",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
},
{
"id": 10,
"name": "J",
"icon": "\uf007",
"radius": 20,
"fill": "#d9d9d9",
"stroke": "#969696"
}
],
"links": [
{
"source": 1,
"target": 2
},
{
"source": 1,
"target": 5
},
{
"source": 1,
"target": 6
},
{
"source": 2,
"target": 3
},
{
"source": 2,
"target": 7
}
,
{
"source": 3,
"target": 4
},
{
"source": 8,
"target": 3
}
,
{
"source": 4,
"target": 5
}
,
{
"source": 4,
"target": 9
},
{
"source": 5,
"target": 10
}
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="style.css">
<body>
<img src = "http://www.webservices.nl/wp-content/uploads/logo_FRISS_rgb72dpi_port_txt.png">
<div id = "controls">
<div class="row">
<div class="col-sm-4">
<button type="button" class="btn btn-success btn-xs" id = "btn1">remove</button>
</div>
<div class="col-sm-6">
<input type="range" name="points" min="0" max="10" step = ".25" id = "id1">
</div>
</div>
</div>
<div id = "id2">value</div>
</body>
<link rel = "stylesheet" href = "http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://d3js.org/d3.v4.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Architects+Daughter" rel="stylesheet">
<script>
// see also
// http://jsfiddle.net/JSDavi/qvco2Ljy
// http://bl.ocks.org/mbostock/2675ff61ea5e063ede2b5d63c08020c7
// http://bl.ocks.org/mbostock/950642
// http://fontawesome.io/cheatsheet/
// https://jsfiddle.net/t4vzg650/6/ collapse example
// http://stackoverflow.com/questions/8986702/d3-js-is-it-possible-to-animate-between-a-force-directed-graph-and-a-node-link
// Lars Kotthoff
// https://www.airpair.com/javascript/posts/d3-force-layout-internals
// bounded force layout
// http://blockbuilder.org/FrissAnalytics/a7c3f6b1b99eb44563b590694fd6e90e
// chart dimensions
var width = 1024, height = 800, radius = 200;
// set up svg
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform)
}))
.append("g");
var forceLink = d3.forceLink().id(function(d) { return d.id; });
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("data.json", function(error, graph) {
var link = svg.append("g")
.style("stroke", "#aaa")
.selectAll("line")
.data(graph.links)
.enter().append("line");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", function(d){return +d.radius})
.style("fill", function(d){return d.fill})
.style("stroke", function(d){return d.stroke})
.style("stroke-width", "1px")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var label = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.style('font-family','FontAwesome')
.style('font-size','20px')
.text(function (d) {return d.icon;})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
//update link positions
link
.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; });
// update node positions
// note in this example we bound the positions
node.attr("cx", function(d) {
return d.x = Math.max(radius, Math.min(width - radius, d.x));
})
.attr("cy", function(d) {
return d.y = Math.max(radius, Math.min(height - radius, d.y));
});
// update label positions
label
.attr("x", function(d) { return d.x; })
.attr("y", function (d) { return d.y; })
.style("font-size", "20px").style("fill", "#4393c3");
}
// when the input range changes update the circle
d3.select("#id1").on("input", function() {
var value = +this.value;
d3.select("#id2").html(value);
});
});
$("#btn1").on("click", function(){
d3.selectAll(".nodes").remove();
})
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;
}
</script>
body {
font-family: 'Architects Daughter', cursive;
}
svg{
position: relative;
background-color: #000000;
z-index:0;
}
#controls{
position: absolute;
top:10px;
left:10px;
z-index:1;
color:white;
}
#id2{
position: absolute;
bottom:10px;
left:10px;
font-size:26px;
z-index:1;
color:red;
}
img{
position: absolute;
top:10px;
right:-60px;
z-index:1;
height:70px
}
button, input{
display: inline-block;
}
.nodes, .labels:hover{
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb:hover{
cursor: pointer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment