console
let green1 = "#DBFBF0";
let green2 = "#AAE5D3";
let green3 = "#68d2b0";
let green4 = "#E9FCF5";
let duration = 100;
let linkRadius = 300;
let grayLinkWidth = 3;
let bubbleNumber = 10;
let lineDropRate = 0.5;
let radiusMin = 5;
let radiusMax = 15;
let multiplier = 1.5;
let screenWidth = window.screen.width;
let screenHeight = window.screen.height;
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
let nodes = d3.range(bubbleNumber).map((d, i) => ({
cx: getRandom(0, screenWidth),
cy: getRandom(0, screenHeight),
vx: getRandom( - 100, 100) / 25,
vy: getRandom( - 100, 100) / 25,
r: getRandom(multiplier * radiusMin, multiplier * radiusMax),
id: i
}));
let svg = d3.select("svg")
.style('width', screenWidth)
.style('height', screenHeight)
.style('margin-left', -screenWidth / 2)
.style('margin-top', -screenHeight / 2)
let svg_nodes = svg.append('g').selectAll('circle')
.data(nodes)
.enter()
.append('circle')
.attr('cx', d => d.cx)
.attr('cy', d => d.cy)
.attr('r', d => d.r)
.attr('fill', '#F3F3F3');
let svg_lines = svg.append('g');
let ticked = () => {
let innerWidth = window.innerWidth;
let innerHeight = window.innerHeight;
svg_nodes.transition().duration(0)
.attr('cx', d => {
if (d.cx + d.r < ((screenWidth - innerWidth) / 2)) {
d.cx += innerWidth + 2 * d.r;
}
if (d.cx - d.r > ((screenWidth + innerWidth) / 2)) {
d.cx -= innerWidth + 2 * d.r;
}
return d.cx;
})
.attr('cy', d => {
if (d.cy + d.r < ((screenHeight - innerHeight) / 2)) {
d.cy += innerHeight + 2 * d.r;
}
if (d.cy - d.r > ((screenHeight + innerHeight) / 2)) {
d.cy -= innerHeight + 2 * d.r;
}
return d.cy;
});
svg_nodes.transition().duration(duration).ease(d3.easeLinear)
.attr('cx', d => (d.cx += d.vx))
.attr('cy', d => (d.cy += d.vy));
let links = [];
for (let i = 0; i < nodes.length - 1; i++) {
for (let j = i + 1; j < nodes.length; j++) {
if (((nodes[i].cx - nodes[j].cx) * (nodes[i].cx - nodes[j].cx) + (nodes[i].cy - nodes[j].cy) * (nodes[i].cy - nodes[j].cy)) < linkRadius * linkRadius && (nodes[i].cx - nodes[i].r) > (screenWidth - innerWidth) / 2 && (nodes[i].cx + nodes[i].r) < (screenWidth + innerWidth) / 2 && (nodes[i].cy - nodes[i].r) > (screenHeight - innerHeight) / 2 && (nodes[i].cy + nodes[i].r) < (screenHeight + innerHeight) / 2 && (nodes[j].cx - nodes[j].r) > (screenWidth - innerWidth) / 2 && (nodes[j].cx + nodes[j].r) < (screenWidth + innerWidth) / 2 && (nodes[j].cy - nodes[j].r) > (screenHeight - innerHeight) / 2 && (nodes[j].cy + nodes[j].r) < (screenHeight + innerHeight) / 2) {
links.push({
source: i,
target: j
});
}
}
}
svg_lines.selectAll('line[ends]')
.data(links, function(d) {
return d ? (d.source + '_' + d.target) : (ends)
})
.transition().duration(duration).ease(d3.easeLinear)
.attr('x1', d => (nodes[d.source].cx))
.attr('y1', d => (nodes[d.source].cy))
.attr('x2', d => (nodes[d.target].cx))
.attr('y2', d => (nodes[d.target].cy))
.style('stroke-width', d => (d.source + d.target) % (1 / lineDropRate) < 1 ? "0": "" + grayLinkWidth);
svg_lines.selectAll('line[ends]')
.data(links, function(d) {
return d ? (d.source + '_' + d.target) : (ends)
})
.enter()
.append('line')
.attr('ends', d => (d.source + '_' + d.target))
.attr('x1', d => (nodes[d.source].cx))
.attr('y1', d => (nodes[d.source].cy))
.attr('x2', d => (nodes[d.target].cx))
.attr('y2', d => (nodes[d.target].cy))
.style('stroke', '#F3F3F3')
.style('stroke-width', '0')
.transition().duration(duration).ease(d3.easeLinear)
.style('stroke-width', d => (d.source + d.target) % (1 / lineDropRate) < 1 ? "0": "" + grayLinkWidth);
svg_lines.selectAll('line[ends]')
.data(links, function(d) {
return d ? (d.source + '_' + d.target) : (ends)
})
.exit()
.attr('ends', null)
.remove();
}
ticked();
setInterval(ticked, duration);
<svg>
</svg>
body {
background: black;
}
svg {
position: fixed;
left: 50%;
right: 50%;
top: 50%;
bottom: 50%;
}
line {}