npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

topology-graph-vue

v1.2.7

Published

High-performance Vue 3 topology graph component library based on PixiJS v8, supporting 50,000+ nodes at 60 FPS

Readme

TopologyGraph Vue

高性能 Vue 3 拓扑图组件库,基于 PixiJS v8,支持 50,000+ 节点 60 FPS 渲染。


特性

  • 🚀 极致性能: LOD 多级降级渲染 + 空间索引裁剪 + 对象池复用 + GPU 预览模式 + Float32Array 力导向迭代,5W+ 节点流畅运行
  • 🎨 丰富图形: 5 种节点形状 (圆/矩形/菱形/六边形/星形) × 3 种线型 (实线/虚线/点线) × 5 种曲线 (直线/贝塞尔/弧线/阶梯HV/VH)
  • 📦 插件化架构: Vue 插件全局注册 + provide/inject 子组件协作,Minimap / SelectionBox / Toolbar 即插即用
  • 🔧 完整 TypeScript: 全程类型覆盖,构建自动生成 .d.ts 声明文件
  • 🎯 丰富交互: 拖拽/缩放/框选(空间索引加速)/多选/撤销重做/搜索聚焦/右键菜单穿透
  • 🎨 双主题: 亮色 / 暗色主题一键切换,运行时可自定义覆盖背景颜色和透明度
  • 📐 4 种布局: 力导向(Barnes-Hut 近似) / 树形(4 方向展开) / 径向 / 环形
  • 📚 LOD 渲染: 4 级降级(detail → normal → simple → dot),缩放时自动切换渲染精度
  • 🔒 节点/连线固定渲染: 通过 forceDetail 属性强制指定节点或连线始终以 detail 模式渲染,无视 LOD 缩放降级,最小半径 10px 保证可见性
  • ✏️ 自定义绘制: 通过 drawNode/drawLine 回调完全覆盖默认渲染,内置图片加载缓存和绘制工具集
  • 🎬 连线动画: 流动 / 快速流动 / 脉冲 / 虚线流动 4 种动画效果
  • 📐 多连线智能递进: 阶梯线(step-hv/step-vh)多连线自动采用几何自适应递进算法,根据节点间短轴长度动态调整折点位置和分离距离,支持高度感知方向判断,彻底解决多连线重叠问题
  • 📝 路径文字排列: 所有曲线类型(直线/贝塞尔/弧线/阶梯线)的文字标签自动沿路径方向排列显示,角度和位置精确计算
  • 📸 高清图片导出: 支持当前视口截图和 100% 比例完整拓扑图导出,完整导出会包含全部节点、连线、背景网格及边缘文字,支持 PNG/JPEG/WebP 格式和高清倍率
  • 🔄 运行时配置热更新: setOptions() 支持动态修改 LOD 阶梯表、性能参数、主题、样式等全部配置项
  • 增量数据操作: 运行时动态添加/删除节点连线 (addNode/removeNodesByIds 等)
  • 🧠 智能历史管理: 大数据量(>10000 节点) 自动禁用撤销以节省内存,快照数量自适应限制
  • 🖱️ 右键事件穿透: 选区框(SelectionBox) 支持右键菜单穿透转发到底层 canvas,完整触发 canvas-contextmenu 事件链路
  • 🔍 聚焦高亮系统: 支持节点/连线聚焦、搜索飞行定位、闪烁提示等高级交互

安装

npm install topology-graph-vue

依赖要求:

  • Node.js >= 18.0.0
  • Vue ^3.3.0 || ^3.4.0 || ^3.5.0
  • PixiJS ^8.17.1 (自动安装)

快速开始

方式一:全局注册 (推荐)

import { createApp } from 'vue'
import TopologyGraph from 'topology-graph-vue'
import 'topology-graph-vue/style.css'

const app = createApp(App)
app.use(TopologyGraph)
app.mount('#app')

全局注册后可在模板中直接使用:

| 组件 | 说明 | |------|------| | <TopologyGraph> | 主组件(画布 + API 注入) | | <TopologyGraphMinimap> | 小地图 | | <TopologyGraphSelectionBox> | 选区操作框(含右键穿透) | | <TopologyGraphToolbar> | 浮动工具栏 |

<template>
  <TopologyGraph @ready="onReady" @canvas-contextmenu="onContextMenu">
    <TopologyGraphMinimap position="right-bottom" />
    <TopologyGraphSelectionBox />
  </TopologyGraph>
</template>

<script setup>
function onReady(api) {
  api.setJsonData({
    nodes: [
      { id: '1', text: '节点1' },
      { id: '2', text: '节点2' },
    ],
    lines: [{ from: '1', to: '2' }],
  })
  api.applyLayout({ type: 'force' })
}

function onContextMenu(event, targetType, target) {
  console.log('右键点击:', { targetType, target })
}
</script>

方式二:按需引入

<script setup>
import { TopologyGraph, Minimap, SelectionBox } from 'topology-graph-vue'
import 'topology-graph-vue/style.css'
</script>

<template>
  <TopologyGraph @ready="onReady">
    <Minimap position="right-bottom" />
    <SelectionBox />
  </TopologyGraph>
</template>

完整示例

<template>
  <div style="width: 100vw; height: 100vh;">
    <TopologyGraph :options="{ theme: 'light', enableHistory: true }" @ready="onReady">
      <GraphToolbar position="top-right" />
      <Minimap :width="200" :height="150" position="right-bottom" />
      <SelectionBox />
    </TopologyGraph>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { TopologyGraph, Minimap, SelectionBox, GraphToolbar } from 'topology-graph-vue'
import 'topology-graph-vue/style.css'

const graphApi = ref(null)

function onReady(api) {
  graphApi.value = api

  // 设置数据(兼容 relation-graph JSON 格式)
  api.setJsonData({
    nodes: [
      { id: '1', text: '节点A', color: '#1890ff' },
      { id: '2', text: '节点B', color: '#52c41a' },
      { id: '3', text: '节点C' },
    ],
    lines: [
      { from: '1', to: '2', showEndArrow: true },
      { from: '2', to: '3' },
    ],
  })

  // 应用力导向布局
  api.applyLayout({ type: 'force' })

  // 运行时动态调整 LOD 配置
  api.setOptions({
    lodTiers: [
      { minScale: 1.0, nodeMode: 'detail', lineMode: 'detail', nodeDowngradeThreshold: 200, lineDowngradeThreshold: 400 },
      { minScale: 0.3, nodeMode: 'normal', lineMode: 'normal', nodeDowngradeThreshold: 500, lineDowngradeThreshold: 1000 },
      { minScale: 0.2, nodeMode: 'simple', lineMode: 'simple', nodeDowngradeThreshold: 2000, lineDowngradeThreshold: 4000 },
      { minScale: 0.008, nodeMode: 'dot', lineMode: 'simple', nodeDowngradeThreshold: 30000, lineDowngradeThreshold: 30000 },
    ],
    perfConfig: {
      nodeTextMinScale: 0.3,
      lineTextMinScale: 0.3,
      hoverColorMinScale: 0.3,
    },
  })

  // 增量添加新节点示例
  const newNode = api.addNode({ id: 'new-1', text: '新增节点', x: 100, y: 200 })
  if (newNode) {
    api.addLine({ from: '1', to: 'new-1', color: '#fa8c16', curve: 'bezier' })
    api.dataUpdated()
  }
}
</script>

API 参考

组件

TopologyGraph(主组件)

内含 PixiJS Canvas + provide/inject 机制。子组件必须放在其插槽内。

Props

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | width | number | — | 画布宽度 (px) | | height | number | — | 画布高度 (px) | | theme | 'light' \| 'dark' | 'light' | 主题 | | devMode | boolean | false | 显示性能面板 | | options | Partial<TopologyOptions> | {} | 运行时配置选项 |

width/height 未设置时默认为父容器尺寸。options 变化时通过 watch(deep) 自动调用 setOptions() 同步。

Events

| 事件 | 参数 | 说明 | |------|------|------| | ready | (api: GraphApi) | 实例就绪,返回完整 API 对象 | | node-click | (node: GraphNode, event: MouseEvent) | 节点点击 | | node-dblclick | (node: GraphNode, event: MouseEvent) | 节点双击 | | node-hover | (node: GraphNode \| null, event: MouseEvent) | 节点悬停 (离开时 node 为 null) | | line-click | (line: GraphLine, event: MouseEvent) | 连线点击 | | line-dblclick | (line: GraphLine, event: MouseEvent) | 连线双击 | | line-hover | (line: GraphLine \| null, event: MouseEvent) | 连线悬停 | | node-delete | (node: GraphNode) | 节点删除 (单个/批量均触发) | | node-drag-start | (node: GraphNode) | 节点拖拽开始 | | node-drag-move | (node: GraphNode, dx, dy) | 节点拖拽中,dx/dy 为累计位移 | | node-drag-end | (node: GraphNode, dx, dy) | 节点拖拽结束 | | selection-change | (selectedIds: Set<string>) | 选中变化 (含 Esc 取消) | | selectionbox-show | (bounds: SelectionBounds, nodeIds: string[]) | 选区框显示 | | selectionbox-close | — | 选区框关闭 | | canvas-click | (event, targetType, target?) | 画布点击 (区分 node/line/canvas) | | canvas-contextmenu | (event, targetType, target?) | 画布右键(含选区框穿透) | | canvas-drag-start | — | 画布拖拽开始 | | canvas-drag-move | (dx, dy) | 画布拖拽中 | | canvas-drag-end | (dx, dy) | 画布拖拽结束 | | canvas-zoom | (scale: number) | 缩放结束 | | viewport-change | (viewport: Viewport) | 视口变化 | | data-change | ({ nodes: number, lines: number }) | 数据变化 |

canvas-contextmenu 事件穿透: 当用户在选区框(SelectionBox)上右键时,事件会自动穿透到 canvas 层并正确触发此事件。事件参数中的 targetType'node'/'line'/'canvas' 之一,可据此区分右键目标。

Slots

| 插槽 | 说明 | |------|------| | default | 子组件 (Minimap / SelectionBox / GraphToolbar) | | overlay | 自定义叠加层 DOM |


Minimap(小地图)

独立 Canvas 2D 离屏渲染的缩略图导航。支持拖拽平移 + 滚轮缩放 + 点击跳转。

Props

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | width | number | 200 | 宽度 (px) | | height | number | 150 | 高度 (px) | | position | MinimapPosition | 'right-bottom' | 位置 | | dotSize | number | — | 节点半径覆盖 (px) | | viewportColor | string | — | 视口框颜色 | | backgroundColor | string | — | 背景颜色 |

MinimapPosition 可选值: 'left-top' \| 'right-top' \| 'left-bottom' \| 'right-bottom' \| 'top-center' \| 'bottom-center' \| 'left-center' \| 'right-center'


SelectionBox(选区操作框)

框选节点后显示操作面板,含选中计数、删除按钮、拖拽移动功能。无需绑定 props/events,通过 inject 自动获取 API。

核心特性:

  • 支持 Shift+左键框选 或 直接左键框选两种模式
  • 工具栏区域排除(删除按钮不触发穿透)
  • 右键事件穿透: 在选区框非工具栏区域右键时,自动将 contextmenu 事件转发到底层 canvas,触发完整的 canvas-contextmenu 事件链路
  • 滚轮事件穿透: 在选区框非工具栏区域滚动鼠标滚轮时,自动将 wheel 事件转发到底层 canvas,支持正常缩放画布

Slots (作用域)

| 插槽 | 参数 | 说明 | |------|------|------| | toolbar | { selectedCount, isDragging, onDelete } | 工具栏扩展 | | default | { selectedCount, isDragging, onDelete } | 内容区域扩展 |


GraphToolbar(浮动工具栏)

14 个功能按钮的悬浮工具栏,可自定义布局和方位定位。

Props

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | position | string | 'top-right' | 8 个方位之一 | | direction | 'horizontal' \| 'vertical' | 'horizontal' | 排列方向 | | layout | ButtonId[][] | 见下方 | 按钮二维数组布局 | | showExport | boolean | true | 显示导出按钮 | | showLayout | boolean | true | 显示布局按钮 |

默认 layout:

[
  ['zoomIn', 'scale', 'zoomOut'],
  ['fitView', 'resetView', 'fullscreen'],
  ['force', 'tree:top', 'tree:bottom', 'tree:left', 'tree:right', 'radial', 'circle'],
  ['download', 'import'],
]

ButtonId 全部可选值:

| ID | 功能 | ID | 功能 | |----|------|-----|------| | zoomIn | 放大 | resetView | 重置视图 | | zoomOut | 缩小 | fullscreen | 全屏切换 | | scale | 缩放百分比显示 | force | 力导向布局 | | fitView | 适应全部 | tree:top/bottom/left/right | 树形 4 方向 | | download | 导出图片 | circle | 环形布局 | | import | 导入 JSON | radial | 径向布局 |


GraphApi (@ready 回调返回)

@ready 事件返回的完整 API 对象,按 14 个功能类别组织:

一、响应式状态 & 性能监控

| 属性 | 类型 | 说明 | |------|------|------| | stats | Ref<PerfStats> | 性能统计 (fps/frameTime/renderTime/nodeCount/scale 等) | | isDarkTheme | Ref<boolean> | 当前是否暗色主题 | | canUndo | Ref<boolean> | 是否可撤销 | | canRedo | Ref<boolean> | 是否可重做 |

二、视图 & 视口控制

| 方法 | 说明 | |------|------| | resetView() | 重置视图 (scale=1, 居中) | | fitView() | 自适应视图 (包含所有节点) | | zoomIn() | 放大一级 | | zoomOut() | 缩小一级 | | zoomTo(scale?) | 缩放到指定比例 (默认 1.0) | | panBy(dx, dy) | 按偏移量平移 | | getViewport() | 获取当前视口 { x, y, scale, width, height } | | setViewport(x, y, scale) | 设置视口位置和缩放 | | toggleFullscreen() | 全屏切换 | | getViewInfo() | 获取详细视口信息 (含边界范围等) |

三、数据管理(导入/导出)

| 方法 | 说明 | |------|------| | generateTestData(count) | 生成随机测试数据 | | setJsonData(data, append?) | 导入 JSON (兼容 relation-graph 格式,append=true 则追加) | | getJsonData() | 导出当前数据为 JSON | | doExport() | 导出内部格式 { nodes, lines } | | importData(data) | 导入内部格式数据 |

四、数据查询 & 删除

| 方法 | 说明 | |------|------| | getNodes() | 获取所有节点数组 | | getLines() | 获取所有连线数组 | | getNodeById(id) | 按 ID 查找节点 | | getLineById(id) | 按 ID 查找连线 | | removeNodeById(id) | 删除单个节点 (同时删除关联连线) | | removeLineById(id) | 删除单条连线 | | dataUpdated(clearHighlight?) | 强制刷新渲染 (直接修改节点/连线属性后调用)。可选参数 clearHighlight: boolean(默认 false),设为 true 可清除所有 click/hover/focus 高亮状态 |

五、增量数据操作(CRUD)

运行时动态增删节点和连线,无需重新设置全部数据。

| 方法 | 返回值 | 说明 | |------|--------|------| | addNode(nodeData) | GraphNode \| undefined | 添加单个节点 | | addLine(lineData) | GraphLine \| undefined | 添加单条连线 | | addNodes(nodes[]) | GraphNode[] | 批量添加节点 | | addLines(lines[]) | GraphLine[] | 批量添加连线 | | updateNodePosition(id, x, y, animate?) | void | 更新节点坐标(支持动画) | | removeNodesByIds(ids[]) | void | 批量删除节点(含关联边) | | removeLinesByIds(ids[]) | void | 批量删除连线 |

NewNodeData 格式:

interface NewNodeData {
  id: string; text?: string; x?: number; y?: number;
  radius?: number; color?: string; shape?: NodeShape;
  icon?: string; opacity?: number; fixed?: boolean;
}

interface NewLineData {
  id?: string; from: string; to: string;
  text?: string; color?: string; directed?: boolean;
  style?: LineStyle; curve?: LineCurve;
  animation?: LineAnimation; showEndArrow?: boolean;
}

六、布局算法

| 方法 | 说明 | |------|------| | applyLayout(options) | 应用布局算法 |

LayoutOptions 参数:

| 参数 | 类型 | 默认值 | 适用布局 | 说明 | |------|------|--------|----------|------| | type | 'force' \| 'tree' \| 'center' \| 'circle' | 'force' | 全部 | 布局类型 | | iterations | number | 300 | force | 力导向迭代次数 | | repulsion | number | 2000 | force | 节点斥力强度 (推荐 2000~10000) | | springLength | number | 60 | force | 弹簧自然长度 (推荐 50~300) | | springK | number | 0.1 | force | 弹簧刚度系数 (推荐 0.02~0.15) | | damping | number | 0.3 | force | 速度阻尼 (推荐 0.2~0.6) | | direction | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' | tree | 树形展开方向 | | levelDistance | number \| number[] | 120 | tree | 层级间距 (数字或逐层数组) | | minPerWidth | number | 30 | tree | 同层最小间距 | | maxPerWidth | number | 200 | tree | 同层最大间距 | | fixedRootNode | boolean | false | tree | 固定根节点位置 | | startAngle | number | 0 | center/circle | 起始角度 (弧度) | | angleStep | 'auto' \| number | 'auto' | center/circle | 角度间距 |

其他布局方法:

| 方法 | 说明 | |------|------| | startLayout() | 开始自动布局 | | refresh() | 刷新当前布局 |

七、节点交互 & 定位

| 方法 | 说明 | |------|------| | focusOnNode(nodeId) | 平移+缩放到指定节点居中 (带动画) | | flashNode(nodeId, duration?) | 节点闪烁效果 (默认 600ms) | | searchAndFlyTo(query) | 按文本搜索节点并飞向首个匹配 | | deleteSelected() | 删除当前选中节点及关联连线 | | moveSelectedNodesBy(dx, dy) | 批量移动选中节点 | | saveCurrentHistory() | 手动保存历史快照 |

八、聚焦高亮

| 方法 | 说明 | |------|------| | focusNodeById(id) | 高亮指定节点及邻边 (清除其他高亮) | | focusLineById(id) | 高亮指定连线 (清除其他高亮) | | cancelFocus() | 清除所有聚焦高亮效果 | | isFocusing() | 是否处于聚焦模式 |

九、选择 & 编辑

| 方法 | 说明 | |------|------| | setEditingNodes(ids) | 设置编辑选框 (空数组隐藏,与选中独立) | | setSelectedIds(ids) | 设置选中 ID 集合 | | selectAll() | 选中全部节点 | | clearSelection() | 清除所有选择(强制清空所有节点的 selected 状态,包括手动设置的 node.selected=true) | | getSelectedNodeIds() | 获取当前选中 ID 集合 (Set<string>) | | clearClickHighlight() | 清除点击高亮状态(clickNodeHighlight / clickLineHighlight),不影响选中状态(selected)和聚焦状态(focus) |

十、样式控制

| 方法 | 说明 | |------|------| | toggleTheme() | 切换亮色/暗色主题 | | setNodeShape(shape) | 设置全局节点形状 ('circle' \| 'rect' \| 'diamond' \| 'hexagon' \| 'star') | | setLineStyle(style) | 设置全局连线样式 ('solid' \| 'dashed' \| 'dotted') | | setLineCurve(curve) | 设置全局曲线类型 ('straight' \| 'bezier' \| 'arc' \| 'step-hv' \| 'step-vh') | | setLineWidth(w) | 设置全局连线宽度倍率 | | setCustomDrawCallbacks(callbacks) | 注册自定义绘制回调 | | applyFilter(color?, text?) | 颜色/文字筛选高亮 |

十一、历史操作(撤销/重做)

| 方法 | 说明 | |------|------| | undo() | 撤销上一步操作 | | redo() | 重做下一步操作 |

大图 (>10000 节点 或 >20000 连线) 自动禁用以节省内存。

十二、导出 & 信息查询

| 方法 | 说明 | |------|------| | downloadAsImage(filename?, options?) | 异步下载当前视口可见区域为图片,返回 Promise<void> | | downloadFullGraphAsImage(filename?, options?) | 异步按 100% 比例完整下载全部拓扑图内容,返回 Promise<void>;包含全部节点、连线、背景网格和边缘文字 | | getViewInfo() | 获取详细视图信息 |

ImageExportOptions:

{
  format?: 'image/png' | 'image/jpeg' | 'image/webp'  // downloadAsImage 默认 'image/webp',downloadFullGraphAsImage 默认 'image/png'
  quality?: number  // 0~1,JPEG/WebP 有效
  scale?: number    // 导出分辨率倍率,用于优化锯齿;完整导出默认 2,最高 4
}

十三、小地图

| 属性/方法 | 类型 | 说明 | |-----------|------|------| | minimapNodes | ShallowRef<MinimapNode[]> | 小地图轻量节点数据 | | getNodesForMinimap() | () => MinimapNode[] | 获取简化节点列表 |

十四、选区框 & 交互模式

| 属性/方法 | 类型 | 说明 | |-----------|------|------| | selBoxVisible | Ref<boolean> | 选区框是否显示 | | selBoxBounds | Ref<SelectionBounds \| null> | 选区边界矩形 | | selBoxCount | Ref<number> | 选区内节点数 | | updateSelectionBounds() | void | 手动触发选区框边界更新 | | setDirectSelection(enabled) | void | 切换直接框选模式 (true=左键直接拖拽; false=需Shift)


TopologyOptions (配置选项)

通过组件 options prop 或 setOptions() 传入,支持运行时热更新。

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | width | number | — | 画布宽度 | | height | number | — | 画布高度 | | theme | 'light' \| 'dark' | 'light' | 主题 | | enableHistory | boolean | true | 启用撤销/重做 | | nodeShape | NodeShape | 'circle' | 默认节点形状 | | nodeSize | number | 60 | 默认节点半径 | | lineStyle | LineStyle | 'solid' | 默认连线样式 | | lineCurve | LineCurve | 'straight' | 默认曲线类型 | | lineWidth | number | 1 | 全局连线宽度倍率 | | lineFontSize | number | 0 | 全局连线文字大小 (0=自动计算) | | zoomStep | number | 0.05 | 缩放步长比例 | | gridColor | string | — | 网格颜色 (CSS 色) | | gridSubColor | string | — | 子网格颜色 | | canvasBackground | string | — | 画布背景颜色 | | focusZoom | number | — | 聚焦目标缩放比例 | | lodTiers | LODTier[] | — | LOD 阶梯表 (覆盖默认 4 级) | | lodConfig | Partial<LODConfig> | — | LOD 详细配置 | | perfConfig | object | — | 性能参数 (见下方) | | themes | Record<'light'\|'dark', Partial<ThemeConfig>> | — | 自定义主题覆盖 | | hoverNodeHighlight | boolean | false | hover 高亮节点 | | hoverNodeLineHighlight | boolean | false | hover 高亮关联边 | | hoverLineHighlight | boolean | false | hover 高亮边 | | clickNodeHighlight | boolean | false | click 高亮节点 | | clickNodeLineHighlight | boolean | false | click 高亮关联边 | | clickLineHighlight | boolean | false | click 高亮边 |

perfConfig 支持的字段:

| 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | nodeTextMinScale | number | 0.5 | 显示节点文字的最小缩放比例 | | lineTextMinScale | number | 0.5 | 显示连线文字的最小缩放比例 | | hoverColorMinScale | number | 0.5 | hover 变色的最小缩放比例 |

lodTiers 示例:

{
  lodTiers: [
    { minScale: 1.0,  nodeMode: 'detail', lineMode: 'detail', nodeDowngradeThreshold: 200,  lineDowngradeThreshold: 400 },
    { minScale: 0.5,  nodeMode: 'normal', lineMode: 'normal', nodeDowngradeThreshold: 500,  lineDowngradeThreshold: 1000 },
    { minScale: 0.2,  nodeMode: 'simple', lineMode: 'simple', nodeDowngradeThreshold: 2000, lineDowngradeThreshold: 4000 },
    { minScale: 0.008, nodeMode: 'dot',   lineMode: 'simple', nodeDowngradeThreshold: 30000, lineDowngradeThreshold: 30000 },
  ]
}

数据类型

GraphNode

interface GraphNode {
  id: string
  text: string              // 显示文本
  x: number                 // 世界坐标 X
  y: number                 // 世界坐标 Y
  radius: number            // 半径 (px, 世界坐标单位)
  color: string             // CSS 颜色字符串
  shape?: NodeShape         // 'circle' | 'rect' | 'diamond' | 'hexagon' | 'star'
  icon?: string             // 图标 URL
  selected?: boolean        // 是否选中
  hovered?: boolean         // 是否悬停
  highlighted?: boolean     // 是否高亮
  borderColor?: string      // 边框颜色
  borderWidth?: number      // 边框宽度
  fontColor?: string        // 文字颜色 (覆盖主题默认)
  fontSize?: number         // 文字大小 px (覆盖默认计算值)
  opacity?: number          // 透明度 0~1
  fixed?: boolean           // 固定位置 (力导向不受影响)
  disabledDrag?: boolean    // 禁止拖拽
  flashing?: boolean        // 正在闪烁
  expanded?: boolean        // 树形展开状态
  isHide?: boolean          // 是否隐藏
  forceDetail?: boolean    // 强制 detail 渲染(无视 LOD 降级,始终显示图标+文字,最小半径 10px)
  data?: any                // 用户自定义数据
}

GraphLine

interface GraphLine {
  id: string
  source: string            // 起始节点 ID
  target: string            // 目标节点 ID
  text?: string             // 连线标签文本
  color: string             // CSS 颜色
  width: number             // 线宽 (世界坐标单位)
  directed: boolean         // 是否有方向
  style?: LineStyle         // 'solid' | 'dashed' | 'dotted'
  curve?: LineCurve         // 'straight' | 'bezier' | 'arc' | 'step-hv' | 'step-vh'
  animation?: LineAnimation // 0=无 | 1=流动 | 2=快速流动 | 3=脉冲 | 4=虚线流动
  animationSpeed?: number   // 动画速度倍率 (默认 1.0)
  fontColor?: string        // 文字颜色
  fontSize?: number         // 文字大小 px
  opacity?: number
  showStartArrow?: boolean  // 起点箭头
  showEndArrow?: boolean    // 终点箭头
  fromJunctionPoint?: JunctionPoint  // 起始接入点
  toJunctionPoint?: JunctionPoint    // 终止接入点
  isHide?: boolean          // 是否隐藏
  forceDetail?: boolean    // 强制始终渲染(无视 LOD 降级,连线+箭头+文字始终可见)
  data?: any
}

JunctionPoint 可选值: 'border' | 'left' | 'right' | 'top' | 'bottom' | 'ltrb' | 'tb' | 'lr'

GraphJsonData (导入格式)

兼容 relation-graph 字段名,支持混合使用:

interface GraphJsonData {
  rootId?: string
  nodes: Array<{
    id: string; text?: string; name?: string; x?; y?; radius?;
    width?: number       // 自动转换为 radius = width / 2
    color?; shape?; icon?; fontColor?; borderColor?
    borderWidth?; opacity?; fixed?; disabledDrag?; data?
  }>
  lines: Array<{
    id?; from?; to?; source?; target?  // 支持 from/to 和 source/target 两套字段名
    text?; label?; color?; fontColor?; fontSize?; lineWidth?
    style?; curve?; animation?; animationSpeed?
    showEndArrow?; showStartArrow?; directed?; data?
  }>
}

自定义绘制

通过 setCustomDrawCallbacks 覆盖节点/边的默认渲染管线:

api.setCustomDrawCallbacks({
  drawNode(ctx) {
    // ctx.node           - 当前节点数据 (GraphNode)
    // ctx.graphics       - PixiJS Graphics 对象
    // ctx.screenX, ctx.screenY - 屏幕坐标 (已应用视口变换)
    // ctx.screenRadius   - 屏幕半径 (已应用缩放)
    // ctx.viewportScale  - 当前缩放比例
    // ctx.nodeMode       - LOD 模式 ('detail' | 'simple' | 'dot')
    // ctx.draw           - DrawHelpers 绘制工具集
    // ctx.drawDefault()  - 回退到默认渲染

    const { node, screenX, screenY, screenRadius, draw } = ctx

    // 示例:带图标的自定义节点
    if (node.icon || node.data?.icon) {
      draw.drawImage(node.icon || node.data?.icon,
        screenX - screenRadius, screenY - screenRadius, screenRadius * 2)
    } else {
      draw.drawCircle(screenX, screenY, screenRadius, node.color)
    }
    draw.drawText(node.text, screenX, screenY + screenRadius + 8, { fontSize: 12 })
  },

  drawLine(ctx) {
    // ctx.line             - 当前边数据 (GraphLine)
    // ctx.graphics         - PixiJS Graphics 对象
    // ctx.sourceScreenX/Y  - 起点屏幕坐标
    // ctx.targetScreenX/Y  - 终点屏幕坐标
    // ctx.lineWidth        - 实际线宽 (考虑缩放)
    // ctx.lineColor        - 线颜色 (PixiJS 数字格式)
    // ctx.viewportScale    - 当前缩放
    // ctx.nodeMode         - LOD 模式
    // ctx.draw             - DrawHelpers
    // ctx.drawDefault()    - 回退默认渲染
  }
})

DrawHelpers (绘制工具集)

| 方法 | 签名 | 说明 | |------|------|------| | loadImage | (url: string) => Promise<boolean> | 加载并缓存图片,返回是否成功 | | drawImage | (url, x, y, w?, h?) => boolean | 绘制已缓存图片,失败返回 false | | drawCircle | (x, y, radius, fillStyle?) => void | 绘制填充圆 | | drawRect | (x, y, w, h, fillStyle?) => void | 绘制填充矩形 | | drawText | (text, x, y, options?) => void | 绘制文字 (fontSize/color/align) |

fillStyle 支持 CSS 颜色 ('#ff0000') 或 PixiJS 数字颜色 (0xff0000)。


事件系统

核心类 TopologyGraphCore (继承 EventEmitter) 支持的全部事件:

节点事件:

| 事件 | 参数 | 说明 | |------|------|------| | node-click | (node, event) | 节点点击 | | node-dblclick | (node, event) | 节点双击 | | node-hover | (node \| null, event) | 节点悬停 (离开时 null) | | node-delete | (node: GraphNode) | 节点删除 | | node-drag-start | (node: GraphNode) | 节点拖拽开始 | | node-drag-move | (node, dx, dy) | 拖拽中累计位移 | | node-drag-end | (node, dx, dy) | 拖拽结束总位移 |

连线事件:

| 事件 | 参数 | 说明 | |------|------|------| | line-click | (line, event) | 连线点击 | | line-dblclick | (line, event) | 连线双击 | | line-hover | (line \| null, event) | 连线悬停 |

选择事件:

| 事件 | 参数 | 说明 | |------|------|------| | selection-change | (selectedIds: Set<string>) | 选择变化 | | selectionbox-show | (bounds, nodeIds[]) | 选区框显示 | | selectionbox-close | — | 选区框关闭 |

视口 & 画布事件:

| 事件 | 参数 | 说明 | |------|------|------| | canvas-click | (event, targetType, target?) | 画布点击 | | canvas-contextmenu | (event, targetType, target?) | 右键菜单(含选区框穿透) | | canvas-drag-start | — | 画布拖拽开始 | | canvas-drag-move | (dx, dy) | 画布拖拽中 | | canvas-drag-end | (dx, dy) | 画布拖拽结束 | | canvas-zoom | (scale: number) | 缩放结束 | | viewport-change | (viewport: Viewport) | 视口变化 |

数据/布局/配置事件:

| 事件 | 参数 | 说明 | |------|------|------| | data-change | ({ nodes, lines }) | 数据变化 | | perf-stats | (stats: PerfStats) | 性能统计更新 | | history-change | ({ canUndo, canRedo }) | 历史状态变化 | | layout-start | — | 布局开始 | | layout-end | — | 布局结束 | | options-change | (options: TopologyOptions) | 配置变更 |

监听方式:

// Vue 组件: @事件名
<TopologyGraph @node-click="(node) => console.log(node.text)" />

// Core API: .on()
graph.on('node-click', (node) => console.log('clicked:', node.id))
graph.on('node-hover', (node) => {
  if (!node) console.log('离开节点')
})

Window 级别拖拽: node-drag-*canvas-drag-* 事件注册在 window 上,即使鼠标离开画布也能持续接收事件,确保拖拽不卡顿。


独立使用(非 Vue 场景)

import { TopologyGraphCore } from 'topology-graph-vue'

// 不依赖 Vue 的纯 JS 用法
const canvas = document.getElementById('canvas') as HTMLCanvasElement
const graph = await TopologyGraphCore.create(canvas, {
  width: 800,
  height: 600,
  theme: 'light',
})

graph.setJsonData({ nodes: [...], lines: [...] })
graph.applyLayout({ type: 'force' })
graph.fitView()

graph.on('node-click', (node) => console.log('clicked:', node.id))

Composable API

import { useTopologyGraph } from 'topology-graph-vue'

用于自定义集成的 composable。一般不需要手动使用,TopologyGraph 组件内部已经集成。


构建产物

dist/
├── index.js                # ESM bundle
├── index.umd.cjs           # UMD bundle (CommonJS 兼容)
├── topology-graph-vue.css  # 样式文件
└── types/                  # TypeScript 声明文件
    ├── *.d.ts              # 所有类型声明
    └── components/*.d.ts   # 组件声明

独立 HTML 打包 (Demo/预览):

dist-standalone/index.html   # 单文件 HTML (内联所有 JS/CSS/SVG)
npm run build:standalone     # 构建
npm run preview:standalone   # 预览

开发

# 安装依赖 (Node >= 18, 推荐 Volta 固定 20.20.0)
npm install

# 开发服务器 (端口 5173)
npm run dev

# 库模式构建 (ESM + UMD)
npm run build

# 独立 HTML 构建
npm run build:standalone

# 类型检查
npm run typecheck

# 代码质量
npm run lint        # ESLint 检查 + 自动修复
npm run format      # Prettier 格式化

# 发布 (standard-version 生成 CHANGELOG)
npm run release

性能调优指南

LOD 阶梯表调优

LOD (Level of Detail) 根据缩放比例自动切换渲染精度:

| minScale | 节点模式 | 边模式 | 效果 | |----------|---------|--------|------| | >= 1.0 | detail (详情) | detail (详情) | 显示图标/文字/边框/箭头 | | >= 0.5 | normal (普通) | normal (普通) | 仅文字,无边框细节 | | >= 0.2 | simple (简化) | simple (简化) | 无文字,纯色填充 | | < 0.008 | dot (点状) | simple | 极小圆点,大量节点场景 |

forceDetail 强制渲染:

dot/simple 模式下,可通过 forceDetail 属性强制指定节点或连线始终以 detail 模式渲染:

// 节点设置 forceDetail
api.setJsonData({
  nodes: [
    { id: '1', text: '始终详情', forceDetail: true },  // 始终显示图标+文字,最小半径 10px
    { id: '2', text: '普通节点' },                      // 遵循 LOD 降级
  ],
  lines: [
    { from: '1', to: '2', forceDetail: true },         // 始终显示连线+箭头+文字
  ],
})

detail/normal 模式下 forceDetail 不生效(所有节点本身已是 detail 渲染)。仅在 dot/simple 降级模式中起作用。

关键阈值:

  • nodeDowngradeThreshold: 该模式下最大渲染节点数,超出则降级
  • lineDowngradeThreshold: 该模式下最大渲染连线数

力导向参数建议

| 参数 | 默认值 | 节点少 (<500) | 节点多 (>5000) | |------|--------|---------------|----------------| | repulsion | 2000 | 1000~2000 | 5000~10000 | | springLength | 60 | 40~80 | 80~150 | | springK | 0.1 | 0.05~0.1 | 0.02~0.08 | | damping | 0.3 | 0.2~0.3 | 0.4~0.6 | | iterations | 300 | 100~200 | 300~500 |


Changelog

详见 CHANGELOG.md


License

MIT