chart = {
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 });
});
});
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();
}