<div id="chart-container"></div>
html {
overflow: hidden;
}
#chart-container {
height: 200px;
width: 100%;
}
line {
stroke: #999;
stroke-opacity: 0.6;
}
circle {
stroke: #fff;
stroke-width: 1.5px;
}
text {
font-family: sans-serif;
font-size: 8px;
text-shadow: 1px 0px 0px #fff, 0px 1px 0px #fff, 1px 1px 0px #fff, -1px 0px 0px #fff, 0px -1px 0px #fff, -1px -1px 0px #fff;
}
@media (min-width: 700px) {
text {
font-size: 10px;
}
}
@media (min-width: 600px) {
#chart-container {
height: 100vh;
}
}
const scale = d3.scaleOrdinal(d3.schemeCategory10);
const color = d => scale(d.group);
const drag = simulation => {
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;
}
return d3
.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
};
const dataPromise = d3.json(
'https://gist.githubusercontent.com/hayesjosh/7eb3ad4c1015ebf66fa0a488c0dfcd49/raw/9277b542b6a24d98eeb934b251d7946230db699c/genre3.json'
);
dataPromise.then(data => {
console.clear();
const links = data.links.map(d => ({ ...d }));
const nodes = data.nodes.map(d => ({ ...d }));
const body = document.getElementById('chart-container');
function redraw() {
const height = body.offsetHeight;
const width = window.innerWidth;
const RADIUS = width > 700 ? 8 : 5;
const STRENGTH = width > 700 ? -200 : -100
const LABEL_X_OFFSET = RADIUS + 4;
const LABEL_Y_OFFSET = RADIUS + 4;
const simulation = d3
.forceSimulation(nodes)
.force('link', d3.forceLink(links).id(d => d.id))
.force('charge', d3.forceManyBody().strength(STRENGTH))
.force('center', d3.forceCenter(width / 2, height / 2));
d3.select('svg').remove();
const svg = d3
.select(body)
.append('svg')
.attr('width', width)
.attr('height', height);
const link = svg
.selectAll('line')
.data(links)
.enter()
.append('line')
.attr('stroke-width', d => Math.log(Math.sqrt(d.value)));
const node = svg
.selectAll('circle')
.data(nodes)
.enter()
.append('circle')
.attr('r', RADIUS)
.attr('fill', color)
.call(drag(simulation));
const label = svg
.selectAll('text')
.data(nodes)
.enter()
.append('text')
.text(d => d.id);
simulation.on('tick', () => {
label
.attr('dx', d => minmax(0, width - LABEL_X_OFFSET - 20, d.x - LABEL_X_OFFSET))
.attr('dy', d => minmax(RADIUS + LABEL_Y_OFFSET, height - LABEL_Y_OFFSET - RADIUS * 2, d.y - LABEL_Y_OFFSET));
node.attr('cx', d => minmax(0, width - RADIUS * 2, d.x)).attr('cy', d => minmax(0, height - RADIUS * 2, d.y));
link
.attr('x1', d => minmax(0, width - RADIUS, d.source.x))
.attr('y1', d => minmax(0, height - RADIUS, d.source.y))
.attr('x2', d => minmax(0, width - RADIUS, d.target.x))
.attr('y2', d => minmax(0, height - RADIUS, d.target.y));
});
}
redraw();
// window.addEventListener('resize', redraw);
});
function minmax(min, max, val) {
return Math.max(min, Math.min(max, val));
}
This Pen doesn't use any external CSS resources.