d3.js: Relation Graph
d3.js Tags · d3/d3 · GitHub
D3 by Observable | The JavaScript library for bespoke data visualization
下载或<!-- 引入 D3.js 库 --><!-- 引入 D3.js 库 -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<!-- 引入 D3.js 库 -->
<script src="https://d3js.org/d3.v7.js"></script>
geovinu.json:
{
"nodes": [
{
"id": 0,
"name": "贾宝玉",
"image": "1.jpg",
"group": 1,
"description": "贾国府衔玉而生的公子。"
},
{
"id": 1,
"name": "林黛玉",
"image": "lin.jpg",
"group": 1,
"description": "贾亲密女友。"
},
{
"id": 3,
"name": "薛宝钗",
"image": "bao.jpg",
"group": 1,
"description": "贾府中意女子。"
},
{
"id": 4,
"name": "王熙凤",
"image": "wan.jpg",
"group": 2,
"description": "贾琏的妻子"
}
],
"links": [
{ "source": 0, "target": 1, "value": 8, "relation": "女友" },
{ "source": 0, "target": 3, "value": 7, "relation": "女友" },
{ "source": 1, "target": 3, "value": 7, "relation": "情敌" },
{ "source": 4, "target": 3, "value": 5, "relation": "婶侄" },
{ "source": 4, "target": 1, "value": 5, "relation": "婶妹" },
{ "source": 4, "target": 0, "value": 5, "relation": "婶侄" }
]
}
geovindu.js
// JavaScript Document geovindu.js
const svg = d3.select("#chart")
.attr("width", 800)
.attr("height", 500);
const width = +svg.attr("width");
const height = +svg.attr("height");
const simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(d => d.id).distance(150)) // 调整链接距离
.force("charge", d3.forceManyBody().strength(-300)) // 调整电荷力强度
.force("center", d3.forceCenter(width / 2, height / 2));
const descriptionDiv = d3.select("#description");
d3.json("geovindu.json").then(data => {
console.log("Loaded data:", data);
console.log("Links:", data.links);
// 创建连接线
const link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(data.links)
.enter().append("line")
.attr("stroke-width", d => Math.sqrt(d.value));
//先隐藏
link.style("opacity", 0);
// 创建关系标签文字
const linkLabels = svg.append("g")
.attr("class", "link-labels")
.selectAll("text")
.data(data.links)
.enter().append("text")
.text(d => d.relation)
.attr("font-size", "10px")
.attr("fill", "#333999");
// 创建节点
const node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(data.nodes)
.enter().append("g");
const circles = node.append("circle")
.attr("r", 5)
.attr("fill", d => {
if (d.group === 1) return "red";
else return "blue";
});
const images = node.append("image")
.attr("href", d => d.image)
.attr("x", -5)
.attr("y", -5)
.attr("width", 50)
.attr("height", 50)
.on("mouseover", (event, d) => {
// 显示描述信息
descriptionDiv.style("display", "block")
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY + 10) + "px")
.html(d.description);
// 显示与该节点相关的线条和关系名称标签
link.style("opacity", l => l.source === d || l.target === d ? 1 : 0);
linkLabels.style("opacity", l => l.source === d || l.target === d ? 1 : 0);
})
.on("mouseout", () => {
// 隐藏描述信息
descriptionDiv.style("display", "none");
// 隐藏所有线条和关系名称标签
link.style("opacity", 0);
linkLabels.style("opacity", 0);
})
.on("error", (error, d) => {
console.error(`Error loading image for ${d.name}:`, error);
});
const labels = node.append("text")
.text(d => d.name)
.attr('x', 6)
.attr('y', 3)
.on("mouseover", (event, d) => {
// 显示描述信息
descriptionDiv.style("display", "block")
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY + 10) + "px")
.html(d.description);
// 显示与该节点相关的线条和关系名称标签
link.style("opacity", l => l.source === d || l.target === d ? 1 : 0);
linkLabels.style("opacity", l => l.source === d || l.target === d ? 1 : 0);
})
.on("mouseout", () => {
// 隐藏描述信息
descriptionDiv.style("display", "none");
// 隐藏所有线条和关系名称标签
link.style("opacity", 0);
linkLabels.style("opacity", 0);
});
const drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
node.call(drag);
simulation
.nodes(data.nodes)
.on("tick", ticked);
simulation.force("link")
.links(data.links);
function ticked() {
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);
// 更新关系标签文字的位置
linkLabels
.attr("x", d => (d.source.x + d.target.x) / 2)
.attr("y", d => (d.source.y + d.target.y) / 2);
node
.attr("transform", d => `translate(${d.x},${d.y})`);
}
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}).catch(error => {
console.error("Error loading data:", error);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简单人物关系图谱 Relation Graph </title>
<link rel="shortcut icon" href="/favicon.ico">
<meta content="Relation Graph 涂聚文,Geovin Du,塗聚文,geovindu,捷为工作室" name="keywords">
<meta content="Relation Graph 涂聚文,Geovin Du,塗聚文,geovindu,捷为工作室" name="description">
<script src="./d3/7.9.0/d3.js"></script>
<style>
.node circle {
fill: #cccccc;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
cursor: pointer;
}
.links {
fill: none;
stroke: #999fff;
stroke-width: 2px;
opacity: 1; /* 初始时连接线隐藏 */
transition: opacity 0.3s;
}
.link-labels text {
font-size: 10px;
fill: #333999;
opacity: 0; /* 初始时关系名称标签隐藏 */
transition: opacity 0.3s;
}
#description {
position: absolute;
background-color: white;
border: 1px solid black;
padding: 10px;
display: none;
}
</style>
</head>
<body>
<svg id="chart" width="800" height="600"></svg>
<div id="description"></div>
<script type="text/javascript" src="geovindu.js">
</script>
</body>
</html>
输出: