Skip to content

Instantly share code, notes, and snippets.

@cyrilcherian
Last active November 27, 2015 01:25
Show Gist options
  • Save cyrilcherian/e98966fa035505879ada to your computer and use it in GitHub Desktop.
Save cyrilcherian/e98966fa035505879ada to your computer and use it in GitHub Desktop.
Collapsible force layout w/ images
{
"customerId": "1",
"email": "test1@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active",
"children": [
{
"customerId": "2",
"email": "test2@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "closed"
},
{
"customerId": "3",
"email": "test3@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "starter"
},
{
"customerId": "4",
"email": "test4@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"customerId": "5",
"email": "test5@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "starter",
"children": [
{
"customerId": "6",
"email": "test6@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "starter"
},
{
"customerId": "7",
"email": "test7@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "suspended"
},
{
"customerId": "8",
"email": "test8@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
}
]
},
{
"customerId": "9",
"email": "test9@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "suspended",
"children": [
{
"customerId": "10",
"email": "test10@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "starter"
}
]
},
{
"customerId": "11",
"email": "test11@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active",
"children": [
{
"customerId": "12",
"email": "test12@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "suspended"
},
{
"customerId": "13",
"email": "test13@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "closed"
}
]
},
{
"customerId": "14",
"email": "test14@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "closed",
"children": [
{
"customerId": "15",
"email": "test15@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "suspended"
}
]
},
{
"customerId": "16",
"email": "test16@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active",
"children": [
{
"customerId": "17",
"email": "test17@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "closed",
"children": [
{
"customerId": "18",
"email": "test18@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "closed"
},
{
"customerId": "19",
"email": "test19@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"customerId": "20",
"email": "test20@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "suspended"
},
{
"customerId": "21",
"email": "test21@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "closed"
},
{
"customerId": "22",
"email": "test22@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "suspended"
}
]
}
]
}
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<style>
path.link {
fill: none;
stroke-width: 3px;
}
.node:not(:hover) .nodetext {
display: none;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 6px;
font: 13px sans-serif;
color: white;
background-color: black;
border-radius: 5px;
pointer-events: none;
}
</style>
<script>
var width = 960;
var height = 800;
var maxNodeSize = 50;
var root;
var force = d3.layout.force();
var tooltip = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
var vis = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
d3.selection.prototype.moveToBack = function() {
return this.each(function() {
var firstChild = this.parentNode.firstChild;
if (firstChild) {
this.parentNode.insertBefore(this, firstChild);
}
});
};
d3.json('data.json', function(json) {
root = json;
root.fixed = true;
root.x = width / 2;
root.y = height / 4;
update();
});
function update() {
var nodes = flatten(root);
var links = d3.layout.tree().links(nodes);
var path = vis.selectAll('path.link')
.data(links, function(d) {
return d.target.id;
});
path.enter().insert('svg:path')
.attr('class', 'link')
.attr('marker-end', 'url(#end)')
.style('stroke', '#eee');
path.exit().remove();
force.nodes(nodes)
.links(links)
.gravity(0.05)
.charge(-1500)
.linkDistance(100)
.friction(0.5)
.linkStrength(function(l, i) {return 1; })
.size([width, height])
.on('tick', tick)
.start();
defs = vis.append('defs');
var clipPath = defs.append('clipPath')
.attr('id', 'clip-circle')
.append('circle')
.attr('r', 25);
var node = vis.selectAll('g.node')
.data(nodes, function(d) {
return d.customerId;
});
var nodeEnter = node.enter().append('svg:g')
.attr('class', 'node')
.attr('transform', function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.attr('filter', function(d){return getFilter(d);})//get teh filter url
.on('mouseover', function(d) {
tooltip.transition()
.duration(200)
.style('opacity', 0.75);
tooltip.html(d.email)
.style('left', (d3.event.pageX - 50) + 'px')
.style('top', (d3.event.pageY - 50) + 'px');
d.scale = 1.5;
tick();
})
.on('mouseout', function(d) {
tooltip.transition()
.duration(200)
.style('opacity', 0);
d.scale = 1;
tick();
})
.on('click', click)
.call(force.drag);
var images = nodeEnter.append('svg:image')
.attr('xlink:href', function(d) {
return d.avatarUrl;
})
.attr('x', function(d) {
return -25;
})
.attr('y', function(d) {
return -25;
})
.attr('height', 50)
.attr('width', 50)
.attr('clip-path', 'url(#clip-circle)');
node.exit().remove();
path = vis.selectAll('path.link');
node = vis.selectAll('g.node');
function tick() {
path.attr('d', function(d) {
var dx = d.target.x - d.source.x;
var dy = d.target.y - d.source.y;
var dr = Math.sqrt(dx * dx + dy * dy);
return 'M' + d.source.x + ','
+ d.source.y
+ 'A' + dr + ','
+ dr + ' 0 0,1 '
+ d.target.x + ','
+ d.target.y;
});
node.attr('transform', nodeTransform);
}
path.moveToBack();
}
function getFilter(d){
if (d.state === 'active') {
return makeFiter('drop-shadow-blue','blue');
} else if (d.state === 'starter') {
return makeFiter('drop-shadow-green','green');
} else if (d.state === 'suspended') {
return makeFiter('drop-shadow-yellow','yellow');
} else if (d.state === 'closed') {
return makeFiter('drop-shadow-red','red');
}
}
function makeFiter(id, color){
//make a filter if filter id not present
if (defs.selectAll("#"+id).empty()){
var filter = defs.append('filter')
.attr('id', id)
.attr('height', '130%');
filter.append('feGaussianBlur')
.attr('in', 'SourceAlpha')
.attr('stdDeviation', 3)
.attr('result', 'blur');
filter.append('feOffset')
.attr('in', 'blur')
.attr('result', 'offsetBlur');
filter.append("feFlood")
.attr("in", "offsetBlur")
.attr("flood-color",color)
.attr("flood-opacity", "1")
.attr("result", "offsetColor");
filter.append("feComposite")
.attr("in", "offsetColor")
.attr("in2", "offsetBlur")
.attr("operator", "in")
.attr("result", "offsetBlur");
var feMerge = filter.append('feMerge');
feMerge.append('feMergeNode')
.attr('in', 'offsetBlur')
feMerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
}
return 'url(#'+id+")"
}
function nodeTransform(d) {
if (!d.scale) {
d.scale = 1;
}
d.x = Math.max(maxNodeSize, Math.min(width - (d.imgwidth / 2 || 16), d.x));
d.y = Math.max(maxNodeSize, Math.min(height - (d.imgheight / 2 || 16), d.y));
return "translate(" + d.x + "," + d.y + ")scale(" + d.scale + ")";
}
function click(d) {
if (d3.event.defaultPrevented) {
return;
}
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
function flatten(root) {
var nodes = [];
var i = 0;
function recurse(node) {
if (node.children) {
node.children.forEach(recurse);
}
if (!node.id) {
node.id = ++i;
}
nodes.push(node);
}
recurse(root);
return nodes;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment