Skip to content

Instantly share code, notes, and snippets.

@gmlnchv
Created February 25, 2016 21:13
Show Gist options
  • Save gmlnchv/80dd206440cca39800b8 to your computer and use it in GitHub Desktop.
Save gmlnchv/80dd206440cca39800b8 to your computer and use it in GitHub Desktop.
Template.home.onDestroyed(function () {
if (Observe) {
Observe.stop();
}
});
Template.home.onRendered(function() {
var nodes = Entries.find().fetch();
var layout = new Layout('#entries', nodes);
var radius = 100;
var jitter = 0.5;
var collisionPadding = 4;
Observe = Entries.find().observe({
added: function(document) {
layout.addNode(document);
layout.render();
},
changed: function(newDocument) {
d3.select(`[data-id=${newDocument._id}]`)
.select('.node-likes')
.text(newDocument.likes)
},
removed: function(document) {
d3.select(`[data-id=${document._id}]`)
.remove()
}
});
function Layout(selector, nodes) {
this.nodes = nodes;
// init svg
var svg = d3.select(selector);
// clean up all previous items before render
svg.selectAll('*').remove();
// init force layout
var force = d3.layout.force()
.nodes(this.nodes)
.gravity(0)
.charge(0)
.on('tick', tick);
var circle = svg.selectAll('g');
// auto-adjust to changed window size
resize();
d3.select(window).on('resize', resize);
function getSize() {
var size = {};
size.width = window.innerWidth;
size.height = window.innerHeight - document.getElementById('submit-container').getBoundingClientRect().height;
return size;
}
// tick
function tick(e) {
var dampenedAlpha;
dampenedAlpha = e.alpha * 0.1;
// Most of the work is done by custom gravity and collide functions
circle
.each(gravity(dampenedAlpha))
.each(collide(jitter))
.attr('transform', function(d) {
// var x = Math.max(radius, Math.min(getSize().width - radius, d.x));
// var y = Math.max(radius, Math.min(getSize().height - radius, d.y));
//
return `translate(${d.x}, ${d.y})`;
});
}
function gravity(alpha) {
var ax, ay, cx, cy;
cx = getSize().width / 2;
cy = getSize().height / 2;
ax = alpha / 8;
ay = alpha;
return function(d) {
d.x += (cx - d.x) * ax;
return d.y += (cy - d.y) * ay;
};
}
function collide(jitter) {
return function(d) {
return nodes.forEach(function(d2) {
var distance, minDistance, moveX, moveY, x, y;
if (d !== d2) {
x = d.x - d2.x;
y = d.y - d2.y;
distance = Math.sqrt(x * x + y * y);
minDistance = radius * 2 + collisionPadding;
if (distance < minDistance) {
distance = (distance - minDistance) / distance * jitter;
moveX = x * distance;
moveY = y * distance;
d.x -= moveX;
d.y -= moveY;
d2.x += moveX;
return d2.y += moveY;
}
}
})
}
}
// resize svg and force layout when screen size change
function resize() {
var width = getSize().width;
var height = getSize().height;
svg.attr('width', width).attr('height', height);
force.size([width, height]).resume();
}
// dynamically update the graph
this.render = function() {
// add nodes
circle = circle
.data(force.nodes(), d => d._id)
// .call(force.drag);
circle
.enter()
.insert('g')
.attr('data-id', d => d._id)
.attr('class', 'node')
.each(function(d) {
d3.select(this).append('circle')
.attr('r', radius - .75)
.style('fill', '#fff')
d3.select(this).append('text')
.attr('class', 'node-name')
.attr('dy', '-3em')
.style('text-anchor', 'middle')
.text(d => d.name)
.style('fill', '#000')
d3.select(this).append('text')
.attr('class', 'node-likes')
.attr('dy', '3em')
.style('text-anchor', 'middle')
.text(d => d.likes)
.style('fill', '#000')
})
.on('click', () => force.stop())
circle.exit().remove();
force.start();
};
// graph data manipulation
this.addNode = function(doc) {
this.nodes.push(doc);
};
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment