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.1.1

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),缩放时自动切换渲染精度
  • ✏️ 自定义绘制: 通过 drawNode/drawLine 回调完全覆盖默认渲染,内置图片加载缓存和绘制工具集
  • 🎬 连线动画: 流动 / 快速流动 / 脉冲 / 虚线流动 4 种动画效果
  • 📸 高清截图导出: RenderTexture 精确裁剪视口区域,支持 PNG/JPEG/WebP 格式,自动适配设备像素比
  • 🔄 运行时配置热更新: setOptions() 支持动态修改 LOD 阶梯表、性能参数、主题、样式等全部配置项
  • 🧠 智能历史管理: 大数据量(>10000 节点) 自动禁用撤销以节省内存,快照数量自适应限制

安装

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">
    <TopologyGraphMinimap position="right-bottom" />
    <TopologyGraphSelectionBox />
  </TopologyGraph>
</template>

方式二:按需引入

<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>

完整示例

<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: '节点1' },
      { id: '2', text: '节点2' },
      { id: '3', text: '节点3' },
    ],
    lines: [
      { from: '1', to: '2' },
      { from: '2', to: '3' },
      { from: '1', 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,
    },
  })
}
</script>

<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>

设计亮点

| 特性 | 实现 | |------|------| | Core 层解耦 | src/core/ 零 Vue 依赖,可通过 TopologyGraphCore 在非 Vue 环境独立使用 | | 按需渲染 | 非持续 rAF,通过 requestRender(force) 调度,仅变化时重绘 | | GPU 预览模式 | Pan/Zoom 时只改 Container transform,零 CPU 渲染开销 | | 空间索引加速 | 均匀网格哈希 (SpatialGrid) 用于视口裁剪和碰撞检测 | | 力导向性能 | Float32Array 存储速度 + Barnes-Hut 四叉树近似 O(n log n) | | 对象池复用 | Graphics(1000) + Text(500),大场景零 GC 抖动 | | 兼容迁移 | JSON 格式兼容 relation-graph |


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 }) | 数据变化 |

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。

Slots (作用域)

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


GraphToolbar(浮动工具栏)

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

Props

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

默认 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 对象,约 50 个成员。

响应式状态

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

数据操作

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

数据查询与删除

| 方法 | 说明 | |------|------| | getNodes() | 获取所有节点数组 | | getLines() | 获取所有连线数组 | | getNodeById(id) | 按 ID 查找节点 | | getLineById(id) | 按 ID 查找连线 | | removeNodeById(id) | 删除节点 (同时删除关联连线) | | removeLineById(id) | 删除连线 | | dataUpdated() | 强制刷新渲染 (直接修改节点/连线属性后调用) |

视图控制

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

布局

| 方法 | 说明 | |------|------| | 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 | 角度间距 |

聚焦与搜索

| 方法 | 说明 | |------|------| | focusOnNode(nodeId) | 平移+缩放到指定节点居中 (带动画) | | flashNode(nodeId, duration?) | 节点闪烁效果 (默认 600ms) | | searchAndFlyTo(query) | 按文本搜索节点并飞向首个匹配 |

选择管理

| 方法 | 说明 | |------|------| | selectAll() | 选中全部节点 | | clearSelection() | 清除选择 | | deleteSelected() | 删除当前选中节点及关联连线 | | moveSelectedNodesBy(dx, dy) | 批量移动选中节点 | | saveCurrentHistory() | 手动保存历史快照 |

聚焦高亮

| 方法 | 说明 | |------|------| | focusNodeById(id) | 高亮指定节点 (清除其他节点高亮) | | focusLineById(id) | 高亮指定连线 (清除其他连线高亮) | | cancelFocus() | 清除所有聚焦高亮 | | isFocusing() | 是否处于聚焦状态 | | setEditingNodes(ids) | 设置编辑选框 (空数组隐藏,与选中状态独立) |

样式控制

| 方法 | 说明 | |------|------| | 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?) | 下载当前视口可见区域为图片 |

ImageExportOptions:

{
  format?: 'image/png' | 'image/jpeg' | 'image/webp'  // 默认 'image/webp'
  quality?: number  // 0~1,JPEG/WebP 有效 (默认 0.85)
}

使用 RenderTexture 精确裁剪到视口可见区域,输出分辨率 = 视口逻辑尺寸 × 设备像素比 (dpr)。无论世界坐标分布多广,导出的图片始终与屏幕所见一致。

配置方法

| 方法 | 说明 | |------|------| | setOptions(options) | 动态修改配置 (运行时生效,覆盖默认值) | | updateLODConfig(config) | 更新 LOD 详细配置 | | setDirectSelection(enabled) | 设置直接框选模式 (true=左键拖拽框选; false=Shift+左键) |

小地图

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

选区框状态

| 属性 | 类型 | 说明 | |------|------|------| | selBoxVisible | Ref<boolean> | 选区框是否显示 | | selBoxBounds | Ref<SelectionBounds> | 选区边界 | | selBoxCount | Ref<number> | 选中节点数 | | updateSelectionBounds() | void | 手动触发选区框更新 |


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 变色的最小缩放比例 | | ... | ... | ... | 更多字段见 DEFAULT_PERF_CONFIG |

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        // 树形展开状态
  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    // 终止接入点
  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(node.text))
graph.on('node-hover', (node) => {
  if (!node) console.log('离开节点')
})

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


底层 API (非 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 (底层桥接)

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

直接操作 DOM 引用的低级 composable。一般不需要手动使用,TopologyGraph 组件内部已经集成。


构建产物

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

构建特性:

  • ESM + UMD 双格式输出
  • vite-plugin-dts 自动生成类型声明
  • vite-plugin-obfuscator 代码混淆 (生产环境)
  • 版本号占位符 __VERSION__ 替换
  • 外部依赖: vue, pixi.js (不打包进 bundle)

独立 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

项目脚本

| 脚本 | 作用 | |------|------| | scripts/clean-js.cjs | 清理 dist 中 vue-tsc 生成的冗余 .js/.map 文件 | | scripts/flatten-dts.cjs | 将嵌套 d.ts 扁平化到 dist/ 根目录 | | scripts/gen-icon.cjs | 代码生成 user.png 图标 (64x64 人形轮廓) |


性能调优指南

LOD 阶梯表调优

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

| minScale | 节点模式 | 边模式 | 效果 | |----------|---------|--------|------| | >= 1.0 | detail (详情) | detail (详情) | 显示图标/文字/边框/箭头 | | >= 0.5 | normal (普通) | normal (普通) | 仅文字,无边框细节 | | >= 0.2 | simple (简化) | simple (简化) | 无文字,纯色填充 | | < 0.008 | 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