30分钟搭建可交互3D社交网络可视化图
从零使用 3d-force-graph 搭建可交互的 3D 社交网络关系图。学习数据加载、节点样式自定义、点击高亮交互和粒子动效,用不到 60 行代码完成一个可直接演示的可视化看板,适合前端/全栈开发者快速上手。

30分钟搭建可交互3D社交网络可视化图
开篇:你是否遇到过这些场景?
做系统架构梳理或团队管理时,常需要向领导展示「系统模块依赖关系」或「团队人员协作网络」。用 PPT 画静态箭头图时,那些密密麻麻的连线叠在一起根本看不清。CTO 曾问我:「能不能做个立体的、能旋转着看的?」
关系型数据的可视化,维度太低反而是一种信息损失。节点之间有没有连线、连线的粗细代表什么、哪些节点是交通枢纽,在平面图里很难一目了然。
今天带你用 3d-force-graph——一个基于 ThreeJS 的轻量级 3D 力导向图组件,从零搭建一个可交互的团队社交网络关系图。学完这篇教程,你能做到:
- 把 JSON 格式的节点和关系数据渲染成 3D 图形
- 自定义节点颜色、大小、悬停标签
- 实现点击节点高亮关联关系、拖拽节点固定位置
- 给连线上添加流动的粒子动效,直观表达「数据流向」
这个仓库 6300+ 星,API 设计极其友好,不需要写一堆 ThreeJS 底层代码,一个 .html 文件就能跑起来。直接开始。
前置条件
- 现代浏览器(Chrome/Edge/Firefox 均可)
- 基础的 HTML + JavaScript 知识,了解 Promise 和箭头函数
- 一个文本编辑器(VS Code 即可)
- 不需要安装 Node.js,用 CDN 方式引入,一个 HTML 文件即可运行
快速上手:3 步让图跑起来
第一步:创建 HTML 骨架
新建文件 index.html,写入以下结构:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>团队社交网络 3D 可视化</title>
<style>
/* 让 canvas 占满整个窗口 */
body { margin: 0; overflow: hidden; background: #0a0a1a; }
</style>
</head>
<body>
<!-- 3D 渲染容器 -->
<div id="3d-graph"></div>
<!-- 引入 3d-force-graph 的 CDN 包 -->
<script src="//cdn.jsdelivr.net/npm/3d-force-graph"></script>
<script>
// 这里会写核心代码
</script>
</body>
</html>
3d-force-graph 通过 CDN 引入后,全局暴露 ForceGraph3D 构造函数。省去 npm install 和打包环节,直接用浏览器打开 html 文件就能看到效果。
第二步:准备一份最简单的关系数据
力导向图需要的数据结构非常直白:一个 nodes 数组表示点,一个 links 数组表示连线。每个 link 的 source 和 target 用节点的 id 关联。
json
{
"nodes": [
{ "id": "alice", "name": "Alice", "group": "前端", "val": 3 },
{ "id": "bob", "name": "Bob", "group": "后端", "val": 5 },
{ "id": "charlie", "name": "Charlie", "group": "后端", "val": 2 },
{ "id": "diana", "name": "Diana", "group": "前端", "val": 4 }
],
"links": [
{ "source": "alice", "target": "bob" },
{ "source": "bob", "target": "charlie" },
{ "source": "alice", "target": "diana" },
{ "source": "charlie", "target": "diana" }
]
}
val 字段控制球体大小(值越大球越大),group 字段用于自动分组着色。
第三步:初始化图表并注入数据
在 HTML 的 <script> 标签里写入:
javascript
const graphData = {
nodes: [
{ id: "alice", name: "Alice", group: "frontend", val: 3 },
{ id: "bob", name: "Bob", group: "backend", val: 5 },
{ id: "charlie", name: "Charlie", group: "backend", val: 2 },
{ id: "diana", name: "Diana", group: "frontend", val: 4 }
],
links: [
{ source: "alice", target: "bob" },
{ source: "bob", target: "charlie" },
{ source: "alice", target: "diana" },
{ source: "charlie", target: "diana" }
]
};
const Graph = ForceGraph3D(document.getElementById('3d-graph'))
.graphData(graphData)
.nodeLabel('name') // 悬停时显示 name 字段
.nodeAutoColorBy('group') // 按 group 字段自动分组着色
.width(window.innerWidth) // 撑满屏幕宽度
.height(window.innerHeight); // 撑满屏幕高度
用浏览器打开 index.html,你会看到深蓝色背景的 3D 空间里,四个彩色球体通过连线互相连接。鼠标拖拽可以旋转视角,滚轮缩放。球体会缓慢移动、互相推拉直到稳定——这就是「力导向」的物理效果在起作用。
原理小课堂:什么是力导向布局?
3d-force-graph 内置了 d3-force-3d 物理引擎,模拟经典力学:
- 节点之间互相排斥(像带同种电荷)
- 有连线的节点之间互相吸引(像弹簧)
- 整个系统有个中心引力,防止节点飞太远
多轮迭代后节点会找到力的平衡位置,关系紧密的节点自然聚在一起。你不需要手动算坐标,这就是力导向图的魅力。
实战示例:完整社交网络可视化
接下来把图表升级成可交互的社交网络看板。目标:
- 点击节点时高亮它直接相连的人和关系,其他淡化
- 拖拽节点后固定位置
- 连线上添加流动的粒子,表达协作方向
Step 1:准备更大的数据集
javascript
const graphData = {
nodes: [
{ id: 'A', name: 'Alice(前端组长)', group: 'frontend', val: 8 },
{ id: 'B', name: 'Bob(后端组长)', group: 'backend', val: 8 },
{ id: 'C', name: 'Charlie(前端开发)', group: 'frontend', val: 3 },
{ id: 'D', name: 'Diana(测试)', group: 'qa', val: 4 },
{ id: 'E', name: 'Ethan(后端开发)', group: 'backend', val: 3 },
{ id: 'F', name: 'Fiona(产品经理)', group: 'pm', val: 6 },
{ id: 'G', name: 'George(运维)', group: 'ops', val: 5 }
],
links: [
{ source: 'A', target: 'C', value: 5 },
{ source: 'A', target: 'B', value: 8 },
{ source: 'A', target: 'F', value: 4 },
{ source: 'B', target: 'E', value: 6 },
{ source: 'B', target: 'G', value: 3 },
{ source: 'C', target: 'D', value: 2 },
{ source: 'D', target: 'E', value: 4 },
{ source: 'E', target: 'G', value: 1 },
{ source: 'F', target: 'G', value: 2 }
]
};
link.value 表示协作频次,用来控制连线粗细和粒子数量。
Step 2:配置自定义样式和交互
javascript
// 颜色映射表,让不同部门有辨识度
const groupColors = { frontend: '#4CAF50', backend: '#2196F3', qa: '#FF9800', pm: '#9C27B0', ops: '#F44336' };
// 记录当前选中的节点
let selectedNode = null;
const Graph = ForceGraph3D(document.getElementById('3d-graph'), {
controlType: 'trackball' // 使用轨迹球控制,支持旋转/缩放/平移
})
.graphData(graphData)
.width(window.innerWidth)
.height(window.innerHeight)
.backgroundColor('#0a0a2e')
// --- 节点样式 ---
.nodeLabel('name')
.nodeColor(node => groupColors[node.group] || '#888888')
.nodeVal('val')
.nodeOpacity(0.85)
.nodeResolution(16) // 球体面数越高越光滑
// --- 连线样式 ---
.linkWidth(link => Math.sqrt(link.value) * 0.8) // 协作越多连线越粗
.linkOpacity(0.4)
.linkDirectionalParticles(link => link.value) // 协作值 = 粒子数量
.linkDirectionalParticleSpeed(0.01) // 粒子流动速度
.linkCurvature(0.25) // 给连线一点弧度,避免重叠
// --- 交互:悬停变色 ---
.onNodeHover((node, prevNode) => {
if (node) { document.body.style.cursor = 'pointer'; }
else { document.body.style.cursor = 'default'; }
})
// --- 交互:点击高亮 ---
.onNodeClick(node => {
// 聚焦相机到被点击节点
Graph.cameraPosition(
{ x: node.x, y: node.y, z: node.z + 20 }, // 相机位置(在节点后方20单位)
{ x: node.x, y: node.y, z: node.z }, // 看的目标点
800 // 动画过渡时间 ms
);
// 更新可见性:只显示关联节点和连线
selectedNode = selectedNode === node ? null : node; // toggle 逻辑
const connectedNodeIds = new Set();
if (selectedNode) {
connectedNodeIds.add(selectedNode.id);
graphData.links.forEach(l => {
if (l.source.id === selectedNode.id) connectedNodeIds.add(l.target.id);
if (l.target.id === selectedNode.id) connectedNodeIds.add(l.source.id);
});
}
Graph
.nodeVisibility(n => !selectedNode || connectedNodeIds.has(n.id))
.linkVisibility(l => !selectedNode || l.source.id === selectedNode.id || l.target.id === selectedNode.id)
.refresh(); // 触发重绘
if (!selectedNode) {
Graph.nodeVisibility(true).linkVisibility(true).refresh();
}
})
// --- 点击空白处重置 ---
.onBackgroundClick(() => {
selectedNode = null;
Graph.nodeVisibility(true).linkVisibility(true).refresh();
Graph.zoomToFit(600, 30);
});
几个关键 API 说明:
.nodeColor(node => ...):传入函数对每个节点返回颜色字符串,根据角色部门、等级动态着色,不用在数据里硬编码。
.linkCurvature(0.25):给连线添加弧度,值 0 是直线,1 是半圆。加了弧度后多条连线重叠时也能区分开。
.linkDirectionalParticles(link.value):在连线上生成等距分布的流动小球,直观表达「协作频次」。数值是 0 就没有粒子,是 3 就有 3 个小球在连线上跑。
.onNodeClick 里的 cameraPosition(x, y, z, lookAt, duration):实现点击聚焦的关键。相机平滑过渡到节点后方一定距离,然后盯着节点看,给用户一种「镜头推近」的感觉。
Step 3:运行体验
保存后刷新浏览器,你可以:
- 鼠标拖拽旋转 3D 视角
- 滚轮缩放
- 点击任何节点,相机推近,周围无关节点和连线自动隐藏
- 点击空白区域,全图恢复,相机自动适配所有节点
- 观察连线上的小粒子沿方向流动
常见问题与踩坑提醒
Q1:节点太多导致渲染卡顿?
力导向计算复杂度较高。几万个节点时建议:
- 设置
.cooldownTicks(150).cooldownTime(10000)限制物理引擎迭代次数 - 关闭指针交互:
.enablePointerInteraction(false),性能大幅提升 - 降低节点分辨率:
.nodeResolution(6)
Q2:力导向布局每次运行结果都不一样?
这是正常的,力导向算法带随机初始速度。需要结果可复现时,初始化前调用 Math.seedrandom('your-seed') 固定随机种子。
Q3:连线箭头怎么加?
用 .linkDirectionalArrowLength(3).linkDirectionalArrowRelPos(0.8),在连线距目标节点 80% 位置画箭头,表达有向关系。
Q4:我想把图嵌入 React/Vue 组件里?
有官方 React 绑定 → react-force-graph,用法几乎一样。Vue 用户可把它当 WebComponent 直接 mount。
总结
从空 HTML 文件到可交互的 3D 社交网络关系图,整个流程:
- 引入 CDN → 不需要脚手架
- 构造
{ nodes, links }数据 → 格式极简 - 链式调用配置样式 →
graphData→nodeColor→onNodeClick - 体验交互效果 → 点击高亮、相机聚焦、粒子流动
3d-force-graph 的设计哲学是「约定优于配置」:不传的参数都有合理默认值,只需覆盖关心的配置。
接下来可以尝试:
- 把实际后端接口数据喂进去,替代静态 JSON
- 尝试
dagMode: 'td'把图变成自上而下的树形/流程图模式 - 看看官方示例中的
custom-node-geometry,把球体替换成自定义 ThreeJS 模型 - 加入 Bloom 后处理效果,做出霓虹灯风格的炫酷可视化
可视化不是炫技,而是让复杂关系一秒被看懂。遇到问题欢迎交流,一起解决。