Skip to content

Instantly share code, notes, and snippets.

@cyrilcherian
Last active November 24, 2015 04:51
Show Gist options
  • Save cyrilcherian/7bc4d2576a92f018b7fe to your computer and use it in GitHub Desktop.
Save cyrilcherian/7bc4d2576a92f018b7fe 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": "active"
},
{
"customerId": "3",
"email": "test3@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"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": "active",
"children": [
{
"customerId": "6",
"email": "test6@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"customerId": "7",
"email": "test7@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"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": "active",
"children": [
{
"customerId": "10",
"email": "test10@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
}
]
},
{
"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": "active"
},
{
"customerId": "13",
"email": "test13@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
}
]
},
{
"customerId": "14",
"email": "test14@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active",
"children": [
{
"customerId": "15",
"email": "test15@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
}
]
},
{
"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": "active",
"children": [
{
"customerId": "18",
"email": "test18@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"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": "active"
},
{
"customerId": "21",
"email": "test21@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
},
{
"customerId": "22",
"email": "test22@example.com",
"avatarUrl": "https://s.gravatar.com/avatar/33636c93c5fa1bc097ea3e8d030997c5?size=80&default=mm",
"state": "active"
}
]
}
]
}
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
</head>
<body>
<style>
path.link {
fill: none;
stroke-width: 3px;
}
.node:not(:hover) .nodetext {
display: none;
}
.d3-tip {
line-height: 1;
padding: 6px;
background: rgba(0, 0, 0, 0.6);
color: #fff;
border-radius: 4px;
}
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<script>
var width = 960;
var height = 800;
var maxNodeSize = 50;
var x_browser = 20;
var y_browser = 25;
var root;
var force = d3.layout.force();
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return d.email;
});
var vis = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
vis.call(tip);
d3.json('data.json', function(json) {
root = json;
root.fixed = true;
root.x = width / 2;
root.y = height / 4;
var defs = vis.insert('svg:defs')
.data(['end']);
defs.enter().append('svg:path')
.attr('d', 'M0,-5L10,0L0,5');
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();
var defs = vis.append('defs');
var clipPath = defs.append('clipPath')
.attr('id', 'clip-circle')
.append('circle')
.attr('r', 25);
var filter = defs.append('filter')
.attr('id', 'drop-shadow')
.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",'red')
.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');
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', 'url(#drop-shadow)')
.on('mouseover', function(d){d.scale = 2;tick();})
.on('mouseout', function(d){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)')
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
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);
}
}
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