跳到主要内容

图可视化与关系网络

概述

图可视化(Graph Visualization)用于展示节点(Node)和边(Edge)组成的关系网络,常见于社交网络、知识图谱、组织架构、依赖分析等场景。

核心概念

图的数据结构

interface GraphNode {
id: string;
label: string;
x?: number;
y?: number;
size?: number;
color?: string;
[key: string]: unknown;
}

interface GraphEdge {
source: string; // 起始节点 ID
target: string; // 目标节点 ID
weight?: number;
label?: string;
}

interface GraphData {
nodes: GraphNode[];
edges: GraphEdge[];
}

图布局算法

布局原理适用场景时间复杂度
力导向(Force-Directed)物理模拟(弹簧+电荷)通用关系网络O(n2)O(n^2) / O(nlogn)O(n \log n)
层次布局(Dagre)DAG 分层排列有向无环图、流程图O(V+E)O(V + E)
圆形布局节点均匀排列在圆上固定拓扑O(n)O(n)
径向布局从中心向外辐射以某节点为中心O(V+E)O(V + E)
树形布局根 → 叶递归排列树形结构O(n)O(n)

力导向布局原理

// 简化的力导向布局实现
interface ForceNode extends GraphNode { vx: number; vy: number }

function forceLayout(nodes: ForceNode[], edges: GraphEdge[], iterations: number): void {
const k = Math.sqrt((800 * 600) / nodes.length); // 理想距离

for (let iter = 0; iter < iterations; iter++) {
// 1. 斥力:每对节点之间互相排斥(库仑力)
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const dx = nodes[j].x! - nodes[i].x!;
const dy = nodes[j].y! - nodes[i].y!;
const dist = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01);
const force = (k * k) / dist; // 斥力与距离成反比
const fx = (dx / dist) * force;
const fy = (dy / dist) * force;
nodes[i].vx -= fx;
nodes[i].vy -= fy;
nodes[j].vx += fx;
nodes[j].vy += fy;
}
}

// 2. 引力:连接的节点之间互相吸引(弹簧力)
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
edges.forEach((e) => {
const source = nodeMap.get(e.source)!;
const target = nodeMap.get(e.target)!;
const dx = target.x! - source.x!;
const dy = target.y! - source.y!;
const dist = Math.sqrt(dx * dx + dy * dy);
const force = dist / k; // 引力与距离成正比
const fx = (dx / dist) * force;
const fy = (dy / dist) * force;
source.vx += fx;
source.vy += fy;
target.vx -= fx;
target.vy -= fy;
});

// 3. 应用速度(带阻尼衰减)
const damping = 0.9 - (iter / iterations) * 0.8;
nodes.forEach((n) => {
n.x! += n.vx * damping;
n.y! += n.vy * damping;
n.vx *= 0.5;
n.vy *= 0.5;
});
}
}

技术选型

底层渲染特点适用场景
AntV G6Canvas/SVG功能全面、布局丰富关系分析、知识图谱
vis.jsCanvas轻量易用简单网络图
Cytoscape.jsCanvas生物信息学背景复杂图分析
D3-forceSVG灵活可定制自定义力导向
Sigma.jsWebGL超大图渲染大规模网络

交互设计

常见交互

// 节点拖拽
function enableNodeDrag(canvas: HTMLCanvasElement, nodes: GraphNode[]): void {
let dragNode: GraphNode | null = null;

canvas.addEventListener('pointerdown', (e) => {
const { offsetX, offsetY } = e;
dragNode = nodes.find((n) =>
Math.hypot(n.x! - offsetX, n.y! - offsetY) < (n.size || 10)
) || null;
});

canvas.addEventListener('pointermove', (e) => {
if (!dragNode) return;
dragNode.x = e.offsetX;
dragNode.y = e.offsetY;
render(); // 重绘
});

canvas.addEventListener('pointerup', () => { dragNode = null; });
}

高亮关联

点击节点时高亮其直接邻居,其余节点灰化:

function highlightNeighbors(graph: GraphData, selectedId: string): void {
const neighborIds = new Set<string>();
neighborIds.add(selectedId);

graph.edges.forEach((e) => {
if (e.source === selectedId) neighborIds.add(e.target);
if (e.target === selectedId) neighborIds.add(e.source);
});

graph.nodes.forEach((n) => {
n.color = neighborIds.has(n.id) ? n.color : '#ddd'; // 非邻居灰化
});
}

大规模图优化

数据规模优化策略
< 500 节点直接渲染,SVG 可用
500 - 5000Canvas 渲染,力导向优化
5000 - 50000WebGL 渲染(Sigma.js),LOD
> 50000聚合/采样,服务端计算布局

常见面试问题

Q1: 力导向布局的原理是什么?

答案

模拟物理系统:所有节点之间有斥力(库仑力,防止重叠),有边连接的节点之间有引力(弹簧力,保持连接)。通过迭代模拟直到系统达到能量最低的平衡态。D3 用 Barnes-Hut 算法把 O(n2)O(n^2) 优化到 O(nlogn)O(n \log n)

Q2: 如何优化大规模图的渲染?

答案

  1. 渲染层:Canvas/WebGL 替代 SVG
  2. 布局计算:Web Worker 或服务端计算
  3. LOD:远距离只显示节点,近距离显示标签和边
  4. 聚合:社区检测后合并密集子图
  5. 虚拟化:只渲染视口内节点

Q3: 图可视化的常见布局算法有哪些?

答案

力导向(通用关系网络)、层次/Dagre(有向无环图)、圆形(等价关系)、径向(以某节点为中心)、树形(层级结构)。选择取决于数据特征和展示目标。

相关链接