Published
Edited
Dec 20, 2019
Insert cell
Insert cell
chart = {
// console.clear();
// CREATING FAKE DATA
const nodes = [];
const links = [];
const numParents = 10;
const minChildren = 3;
const maxChildren = 10;
let parentID = -1;
let childID = -1;
d3.range(numParents).forEach(() => {
parentID++;
const thisParentID = `parent-${parentID}`;
nodes.push({ id: thisParentID, type: "parent" });
const numChildren = Math.floor(
d3.scaleLinear([0, 1], [minChildren, maxChildren])(Math.random())
);
d3.range(numChildren).forEach(() => {
childID++;
const thisChildId = `child-${childID}`;
nodes.push({ id: thisChildId, type: "child" });
links.push({ source: thisParentID, target: thisChildId });
});
});
// DONE WITH FAKE DATA

const height = 600;

const linkForce = d3
.forceLink(links)
.id(d => d.id)
.distance(0)
.strength(1);

const maxForce = -50;

const manyBodyForce = d3
.forceManyBody()
.strength(d => (d.type === "parent" ? maxForce : 0));

const simulation = d3
.forceSimulation(nodes)
.force("link", linkForce)
.force("charge", manyBodyForce)
.force("x", d3.forceX())
.force("y", d3.forceY())
.alpha(0.1)
.alphaDecay(0);

const container = d3.create("div");

const maxRadius = 5;

let timer;

container
.append("button")
.style("width", "100px")
.style("height", "100px")
.text("start")
.on("click", () => {
console.log("click!!");
let duration = 1e3;
let t = 0;
timer = d3.timer(elapsed => {
t = d3.scaleLinear([0, duration], [0, 1])(elapsed);

const distance = d3.scaleLinear([0, 1], [0, 20])(t);
const strength = d3.scaleLinear([0, 1], [0, maxForce])(t);
const radius = d3.scaleLinear([0, 1], [0, maxRadius])(t);

linkForce.distance(distance);

manyBodyForce.strength(d => {
return d.type === "parent" ? maxForce : strength;
});

node.attr("r", d => {
return d.type === "parent" ? maxRadius : radius;
});

if (t >= 1) {
timer.stop();
}
});
});

const svg = container
.append("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]);

const link = svg
.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line");

const node = svg
.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", d => {
return d.type === "parent" ? maxRadius : 0;
})
.attr("fill", d => {
return d.type === "parent" ? "black" : "red";
})
.sort(a => (a.type === "parent" ? 1 : -1));

simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node.attr("cx", d => d.x).attr("cy", d => d.y);
});

invalidation.then(() => {
simulation.stop();
if (timer) {
timer.stop();
}
});

return container.node();
}
Insert cell
d3 = require("d3@5")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more