function blogGraph(graph) {
const width = window.innerWidth;
const height = window.innerHeight;
const sourceRadius = 45;
const entityRadius = 35;
var svg = d3
.select("#networkGraph")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(
d3.zoom().on("zoom", function (event) {
svg.attr("transform", event.transform);
})
)
.append("g");
var simulation = d3
.forceSimulation()
.force(
"link",
d3.forceLink().id(function (d) {
return d.id;
})
)
.force(
"charge",
d3.forceManyBody().strength(-2000).theta(0.5).distanceMax(500)
)
.force(
"collision",
d3.forceCollide().radius(function (d) {
return d.radius;
})
)
.force("center", d3.forceCenter(width / 2, height / 2));
var defs = svg.append("defs");
defs
.append("radialGradient")
.attr("id", "entity-gradient")
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", "50%")
.selectAll("stop")
.data([
{ offset: "50%", color: "#ffffff" },
{ offset: "100%", color: "#CCCCCC" },
])
.enter()
.append("stop")
.attr("offset", function (d) {
return d.offset;
})
.attr("stop-color", function (d) {
return d.color;
});
defs
.append("svg:pattern")
.attr("width", 150)
.attr("height", 150)
.attr("id", "bgPattern")
.append("svg:image")
.data(graph.nodes)
.attr("xlink:href", function (d) {
return "../assets/images/" + d.compressed;
})
.attr("width", 150)
.attr("height", 150)
.attr("x", 0)
.attr("y", -20);
var link = svg
.append("g")
.selectAll("line")
.data(graph.links)
.enter()
.append("line");
link.style("stroke", "#aaa");
var node = svg
.append("g")
.attr("class", "nodes")
// .selectAll("img")
// .data(graph.nodes)
// .enter()
// .append("img")
// .attr("xlink:href", function (d) {
// return "../assets/images/" + d.compressed;
// })
// .attr("width", 150)
// .attr("height", 150)
// .attr("x", -150)
// .attr("y", -150);
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.attr("r", entityRadius)
.call(drag(simulation));
// .attr("fill", "url(#bgPattern)");
node
.append("image")
.attr("xlink:href", function (d) {
return "../assets/images/" + d.compressed;
})
.attr("width", 150)
.attr("height", 150)
.attr("x", -75)
.attr("y", -75);
node
.style("fill-opacity", "0.5")
// .style("fill", "#cccccc")
.style("stroke", "#424242")
.style("stroke-width", "1px");
var label = svg
.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter()
.append("text")
.text(function (d) {
return d.title;
})
.attr("class", "label");
label.style("text-anchor", "middle").style("font-size", function (d) {
return d.title == "technology"
? Math.min(
2 * entityRadius,
((2 * entityRadius - 8) / this.getComputedTextLength()) * 15
) + "px"
: Math.min(
2 * sourceRadius,
((2 * sourceRadius - 8) / this.getComputedTextLength()) * 15
) + "px";
});
label
.on("mouseover", function (d) {
tooltip.html(`${d.title}`);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function (event) {
return tooltip
.style("top", event.pageY - 10 + "px")
.style("left", event.pageX + 10 + "px");
});
node
.on("mouseover", function (d) {
tooltip.html(`${d.title}`);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function (event) {
return tooltip
.style("top", event.pageY - 10 + "px")
.style("left", event.pageX + 10 + "px");
})
.on("mouseout", function () {
return tooltip.style("visibility", "hidden");
});
simulation.nodes(graph.nodes).on("tick", ticked);
simulation.force("link").links(graph.links);
function ticked() {
link
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
node
.attr("cx", function (d) {
return d.x + 5;
})
.attr("cy", function (d) {
return d.y - 3;
});
label
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
});
}
function drag(simulation) {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
var tooltip = d3
.select("body")
.append("div")
.style("position", "absolute")
.style("visibility", "hidden")
.style("color", "white")
.style("padding", "8px")
.style("background-color", "#626D71")
.style("border-radius", "6px")
.style("text-align", "center")
.style("width", "auto")
.text("");
}
var nodesUrl = "https://www.fabianschober.com/json/nodes.json";
var linksUrl = "https://www.fabianschober.com/json/links.json";
Promise.all([d3.json(nodesUrl), d3.json(linksUrl)]).then((res) => {
// console.log(res);
blogGraph({ nodes: res[0], links: res[1] });
});
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="./ForceGraph.js"></script>
</head>
<body>
<div id="networkGraph"></div>
</body>
</html>