Built with blockbuilder.org
forked from lstefano71's block: Dynamic Force
forked from lstefano71's block: Dynamic Force
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="tinycolor.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<div id="graph"></div>
<script>
const width = 1052,
height = 920;
const data = { nodes: [], links: [], clusters: [] };
const maxage = 20000;
const canvas = d3
.select("#graph")
.append("canvas")
.attr("width", width)
.attr("height", height);
const context = canvas.node().getContext("2d");
const delay = 600;
const simulation = d3
.forceSimulation()
.force(
"link",
d3.forceLink().id(d => d.label).distance(d => d.source.r + d.target.r).strength(d => 0.6)
)
.force("collide", d3.forceCollide(d => d.r + 4).iterations(5))
.force("charge", d3.forceManyBody().strength(d => -50))
//.force("center", d3.forceCenter(chartWidth / 2, chartHeight / 2))
.force("x", d3.forceX(width / 2).strength(d => 0.07))
.force("y", d3.forceY(height / 2).strength(d => 0.07))
.on("tick", ticked);
function ticked() {
const now = d3.now();
context.clearRect(0, 0, width, height);
// draw links
context.strokeStyle = "grey";
context.lineCap = "round";
context.lineJoin = "round";
data.links.forEach(function(d) {
d.age = now - d.born;
const opacity = Math.max(0,1-d.age/maxage);
context.beginPath();
context.globalAlpha = opacity;
context.lineWidth = 2+2*opacity;
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// draw nodes
const pcol = tinycolor("#027");
data.nodes.forEach(function(d) {
context.beginPath();
context.moveTo(d.x, d.y);
context.arc(d.x, d.y, d.r, 0, 2 * Math.PI);
d.age = now - d.born;
const opacity = Math.max(0,1-d.age/maxage);
context.fillStyle = pcol.clone().desaturate(100-100*opacity).lighten(60-60*opacity);
context.fill();
});
data.nodes = data.nodes.filter(d => d.age < maxage);
data.links = data.links.filter(l => l.target.age < maxage && l.source.age < maxage);
context.font = '18px sans-serif';
context.fillStyle = "black";
context.globalAlpha = 1;
context.fillText("nodes: " + data.nodes.length + " links: " + data.links.length, width-400, 50);
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
var range = 2;
var extdata = null;
d3.json("data.json",function(err,json) {
extdata = json;
});
function newData(elapsed) {
for (let i = 0; i < range; i++) {
let n = {
label: "l" + i,
r: getRandomInt(16, 30),
cluster: i,
born: d3.now() - getRandomInt(0,maxage/2),
age: 0
};
data.nodes.push(n);
data.clusters.push(n);
const l = getRandomInt(2,10);
for (let j = 0; j < l; j++) {
let f = {
label: "l" + i + "c" + j,
r: getRandomInt(3, 8),
cluster: i,
born: d3.now() - getRandomInt(0,maxage/2),
age: 0
};
data.nodes.push(f);
data.links.push({ source: f.label, target: n.label, born: d3.now() });
}
}
for (let i = 0, t = getRandomInt(2,6); i < t; i++) {
const s = getRandomInt(0, data.nodes.length - 1);
const t = getRandomInt(s, data.nodes.length - s);
data.links.push({
source: data.nodes[s].label,
target: data.nodes[t].label,
born: d3.now()
});
}
update();
d3.timeout(newData, getRandomInt(delay/2,delay*2));
}
d3.timeout(newData, delay);
function update() {
simulation.nodes(data.nodes);
simulation.force("link").links(data.links);
simulation.alpha(0.8).restart();
}
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;
}
function LightenDarkenColor(col,amt) {
var num = parseInt(col,16);
var r = (num >> 16) + amt;
var b = ((num >> 8) & 0x00FF) + amt;
var g = (num & 0x0000FF) + amt;
var newColor = g | (b << 8) | (r << 16);
return newColor.toString(16);
}
</script>
</body>
https://d3js.org/d3.v4.min.js