Skip to content

Instantly share code, notes, and snippets.

@gion
Created February 14, 2020 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gion/7b870122d59492f75d853d9197e77f86 to your computer and use it in GitHub Desktop.
Save gion/7b870122d59492f75d853d9197e77f86 to your computer and use it in GitHub Desktop.
import * as d3 from "d3";
export default class D3BubbleChart {
constructor(domEl, { width, height, nodes = [] }) {
this.domEl = domEl;
this.width = width;
this.height = height;
// and the svg container
this.setup(nodes);
// create the force layout
this.updateForces();
// run the simulation
this.startSimulation();
}
setup = nodes => {
this.svg = d3
.select(this.domEl)
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", `0 0 ${this.width} ${this.height}`);
this.g = this.svg.append("g").attr("class", "node");
this.simulation = d3.forceSimulation();
this.simulation.on("tick", this.tickHandler);
this.updateNodes(nodes);
};
updateForces() {
this.simulation
.force(
"center",
d3
.forceCenter()
.x(this.width * 0.5)
.y(this.height * 0.5)
)
.velocityDecay(0.5)
.force("x", d3.forceX().strength(0.01))
.force("y", d3.forceY().strength(0.01))
.force(
"collide",
d3.forceCollide(d => d.radius * 1.1 + d.strokeWidth)
);
}
startSimulation() {
this.simulation
.nodes(this.nodes)
.alpha(1)
.restart();
}
// handle nodes update
updateNodes = nodes => {
this.svg
.selectAll("circle")
.data(nodes)
.join(
enter =>
enter
.attr("r", d => d.radius)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("opacity", 1),
update =>
update
.attr("fill", d => d.color)
.attr("r", d => d.radius)
.attr("stroke", d => d.stroke)
.attr("stroke-width", d => d.strokeWidth),
exit =>
exit
.attr("r", 0)
.attr("cx", this.width / 2)
.attr("cy", this.height / 2)
.attr("opacity", 0)
.remove()
);
};
// update node positions
tickHandler = d => {
this.svg
.selectAll("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y);
};
// "public" method
// this is how React should send updates to this component
update({ nodes, width, height }) {
this.width = width;
this.height = height;
this.nodes = nodes;
this.svg.attr("viewBox", `0 0 ${this.width} ${this.height}`);
this.updateForces();
this.startSimulation();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment