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

pixi-graph-engine

v0.1.1

Published

High-performance canvas graph engine based on PixiJS 8 + Vue 3 + RBush

Downloads

240

Readme

pixi-graph-engine 组件使用说明

基于 PixiJS + Vue 3 构建的高性能数据流图引擎。
支持 1000+ 节点流畅渲染、字段级连线、自定义节点类型、HTML 编辑叠层。


目录


快速开始

方式一:Vue 插件(推荐)

import { createApp } from 'vue'
import PixiGraphEngine from 'pixi-graph-engine'
import App from './App.vue'

const app = createApp(App)
app.use(PixiGraphEngine)
app.mount('#app')
<!-- 在模板中直接使用 -->
<template>
  <PixiGraphCanvas />
</template>

方式二:按需引入

import { PixiGraphCanvas, MiniMap, Toolbar } from 'pixi-graph-engine'

方式三:底层 API 自定义

import {
  store, addNode, addEdge,
  createNode, createEdge,
  computeLayout,
  createPixiApp, initScene, renderNode, renderEdge,
  rebuildIndex, initEventSystem
} from 'pixi-graph-engine'

架构概览

┌─────────────────────────────────────────────────────────┐
│  Vue 3 UI 层                                            │
│  PixiGraphCanvas / NodeEditor / MiniMap / Toolbar       │
│  PortOverlay / NodeActionsOverlay / NodeHoverToolbar    │
├─────────────────────────────────────────────────────────┤
│  PixiJS Canvas 层(高性能渲染)                          │
│  nodeRenderer  ←→  edgeRenderer                        │
│  scene(container 管理)                                 │
├─────────────────────────────────────────────────────────┤
│  Core 数据层                                            │
│  store(响应式)  node  edge  viewport  port            │
│  nodeTypeRegistry  iconRegistry  layoutConstants        │
├─────────────────────────────────────────────────────────┤
│  Layout Engine                                          │
│  object / array / table / jsonTree / text               │
├─────────────────────────────────────────────────────────┤
│  Interaction 系统                                       │
│  hitTest(rbush)  drag  select  eventSystem            │
└─────────────────────────────────────────────────────────┘

渲染策略:节点背景、header、字段行由 PixiJS 绘制(GPU 加速);
编辑表单、full 模式内容展示、工具栏、连线按钮由 Vue HTML 叠层覆盖。


核心数据模型

NodeModel

interface NodeModel {
  id:     string        // 唯一标识
  type:   NodeType      // 'object' | 'array' | 'table' | 'json' | 'text'
  x:      number        // 世界坐标 X
  y:      number        // 世界坐标 Y
  data:   any           // 节点数据(格式见内置节点类型)

  layout: {
    readonly: { width: number; height: number }  // Canvas 只读尺寸
    edit:     { width: number; height: number }  // 编辑面板尺寸
  }

  state: {
    selected: boolean   // 是否被选中
    hover:    boolean   // 是否悬停
    editing:  boolean   // 是否进入编辑态
  }

  // 可选:节点级外观覆盖(优先级高于类型注册表)
  meta?: {
    icon?:        string   // 图标字符(unicode / emoji / font icon 码点)
    fontFamily?:  string   // 图标字体
    headerColor?: number   // header 徽章颜色(0xRRGGBB)
    label?:       string   // header 显示标签(不填默认显示类型名)
  }

  customType?: string      // 自定义类型名(对应 nodeTypeRegistry 注册 key)
  status?:     NodeStatus  // 'idle' | 'loading' | 'success' | 'error'
}

创建节点:

import { createNode, computeLayout, addNode } from 'pixi-graph-engine'

const node = createNode({
  id: 'node-1',
  type: 'object',
  x: 100,
  y: 200,
  data: {
    name:  { type: 'string', value: 'Alice' },
    score: { type: 'string', value: '95' }
  }
})
node.layout = computeLayout(node)  // 必须:计算并注入布局尺寸
addNode(node)

EdgeModel

interface EdgeModel {
  id:     string
  source: string   // 源节点 id
  target: string   // 目标节点 id

  // 可选:精确到字段行的端口引用
  sourcePort?: PortRef   // { nodeId, portType: 'node'|'field', fieldKey? }
  targetPort?: PortRef
}

创建边:

import { createEdge, addEdge } from 'pixi-graph-engine'

const edge = createEdge({
  id: 'edge-1',
  source: 'node-1',
  target: 'node-2'
})
addEdge(edge)

带端口的精确连线(字段级):

const edge = createEdge({
  id: 'edge-2',
  source: 'node-1',
  target: 'node-2',
  sourcePort: { nodeId: 'node-1', portType: 'field', fieldKey: 'name' },
  targetPort: { nodeId: 'node-2', portType: 'node' }
})

FieldDef(字段类型系统)

Object 节点的 data 字段支持两种格式:

普通格式(key → 值):

data: { name: 'Alice', age: 25 }

FieldDef 格式(key → 带类型标注的字段定义):

data: {
  name:     { type: 'string', value: 'Alice' },
  tags:     { type: 'array',  value: ['admin', 'user'] },
  profile:  { type: 'object', value: { role: 'admin' } },
  metrics:  { type: 'table',  value: { columns: ['month', 'val'], rows: [['Jan', '90']] } },
  raw:      { type: 'json',   value: { source: 'api', ts: 1700000000 } }
}

FieldDef 格式能驱动字段级类型徽章显示(渲染层自动识别 { type, value } 结构)。


内置节点类型

| 类型 | node.type | data 格式 | 渲染样式 | |---|---|---|---| | Object | 'object' | Record<string, any \| FieldDef> | 双栏 Key/Value 行,支持字段类型徽章 | | Array | 'array' | any[] | 索引+值列表行 | | Table | 'table' | { columns: string[], rows: any[][] } | 列头行 + 数据行表格 | | JSON | 'json' | any(任意可序列化对象) | 语法高亮格式化展示 | | Text | 'text' | string \| { text: string } | 等宽字体内容区域 |

各类型 data 示例:

// object
data: { username: { type: 'string', value: 'bob' }, age: { type: 'string', value: '30' } }

// array
data: ['alpha', 'beta', 'gamma']

// table
data: { columns: ['id', 'name'], rows: [['1', 'Alice'], ['2', 'Bob']] }

// json
data: { api: 'v2', status: 'ok', nested: { count: 42 } }

// text
data: { text: 'Hello world\nLine 2' }
// 或者直接字符串
data: 'Hello world'

自定义节点类型

通过 registerNodeType 注册,将自定义类型名绑定到一组配置,节点设置 customType 后自动应用。

import { registerNodeType } from 'pixi-graph-engine'

registerNodeType('my-source', {
  base: 'object',              // 底层数据类型(决定布局算法和编辑器)
  headerBackground: 0x0f766e, // header 徽章颜色
  fontIcon: { icon: '⛁' },   // 自定义图标
  showStatus: true,            // 显示状态指示器(idle/loading/success/error)
  editable: true,              // 显示编辑按钮
  clickBehavior: 'select',     // 'select'(默认)| 'edit'(点击直接进入编辑)
  heightMode: 'compact',       // 'compact'(默认)| 'full'(HTML 全量展示)
  hoverToolbar: {
    position: 'top-end',       // 工具栏位置(见下方说明)
    actions: `<button onclick="...">操作</button>`
  }
})

// 使用时在节点上设置 customType
const node = createNode({
  id: 'n1', type: 'object', x: 0, y: 0,
  customType: 'my-source',
  data: { host: { type: 'string', value: 'db.local' } }
})

NodeTypeConfig 完整选项

interface NodeTypeConfig {
  // ── 必填 ──────────────────────────────────
  base: 'object' | 'array' | 'table' | 'json' | 'text'

  // ── 外观 ──────────────────────────────────
  headerHeight?:    number   // header 高度(px,默认 40)
  headerBackground?: number  // header 徽章背景色(0xRRGGBB)
  fontIcon?: {
    icon:       string       // 图标字符
    fontFamily?: string      // 图标字体名(需预加载)
  }

  // ── 状态 ──────────────────────────────────
  showStatus?: boolean       // 是否显示右上角状态指示器(默认 false)

  // ── 交互 ──────────────────────────────────
  clickBehavior?: 'select' | 'edit'   // 默认 'select'
  editable?:      boolean             // 显示编辑按钮(默认 false)
  hoverToolbar?:  HoverToolbarConfig  // hover 时弹出工具栏

  // ── 内容高度 ──────────────────────────────
  heightMode?:    'compact' | 'full'  // 默认 'compact'
  defaultHeight?: number              // full 模式初始占位高度(默认 200)
}

高度模式 compact vs full

| 模式 | 渲染方式 | 适用场景 | |---|---|---| | compact | 全部在 PixiJS Canvas 中绘制,最多显示 5 行 | 大量节点,性能优先 | | full | Canvas 只绘制 header + 背景,内容区由 HTML overlay 覆盖,高度自适应 | 需要展示全量数据、滚动等 |

// full 模式示例
registerNodeType('detail-table', {
  base: 'table',
  heightMode: 'full',
  defaultHeight: 200  // 初始占位,mount 后自动同步实际高度
})

Hover 工具栏

interface HoverToolbarConfig {
  position?: ToolbarPosition  // 工具栏位置,默认 'top-end'
  actions:   Component | string  // Vue 组件 或 HTML 字符串
}

type ToolbarPosition =
  | 'top-start'    // 节点上方,左对齐
  | 'top-end'      // 节点上方,右对齐(默认)
  | 'top-right'    // 节点右侧,顶部对齐(向右展开)
  | 'top-left'     // 节点左侧,顶部对齐(向左展开)
  | 'bottom-start' // 节点下方,左对齐
  | 'bottom-end'   // 节点下方,右对齐

HTML 字符串模式:

hoverToolbar: {
  position: 'top-end',
  actions: `
    <div style="display:flex;gap:4px;">
      <button onclick="myAction()">操作</button>
    </div>
  `
}

Vue 组件模式(接收 node prop):

import { defineComponent, h } from 'vue'

hoverToolbar: {
  position: 'top-end',
  actions: defineComponent({
    props: ['node'],
    setup(props) {
      return () => h('button', {
        onClick: () => console.log('node id:', props.node.id)
      }, '操作')
    }
  })
}

图标系统

图标优先级:node.meta.icon > 类型注册表 registerNodeTypeIcon > 内置默认。

内置默认(无需配置):

| 类型 | 徽章文字 | |---|---| | object | Obj | | array | Arr | | table | Table | | json | JSON | | text | Str |

注册类型级图标(覆盖内置默认):

import { registerNodeTypeIcon, setDefaultIconFont } from 'pixi-graph-engine'

// 使用 emoji / unicode
registerNodeTypeIcon('object', { icon: '⬡' })

// 使用 icon font(需预加载字体)
setDefaultIconFont('Material Icons')
registerNodeTypeIcon('database', { icon: '\ue1d0' })

// 批量注册
registerNodeTypeIcons({
  api:      { icon: '\ue8b8' },
  service:  { icon: '\ue8b5', fontFamily: 'Material Icons Outlined' }
})

节点级覆盖(优先级最高):

const node = createNode({
  id: 'n1', type: 'object', x: 0, y: 0, data: {},
  meta: {
    icon: '★',
    headerColor: 0xef4444,   // 红色徽章
    label: '特殊节点'
  }
})

节点运行时状态(需 showStatus: true):

// 在注册类型时启用
registerNodeType('my-type', { base: 'object', showStatus: true })

// 运行时修改节点状态
node.status = 'loading'   // 旋转圆环
node.status = 'success'   // 绿色对勾
node.status = 'error'     // 红色叉号
node.status = 'idle'      // 默认灰点

连线与端口系统

端口类型

| PortType | 描述 | |---|---| | 'node' | 整个节点的端口(header 右侧圆点) | | 'field' | 特定字段/行的端口(各行右侧 "+" 按钮) |

只有 object / array / table 类型节点支持字段级端口。

手动连线交互

用户点击节点右侧 "+" 圆形按钮开始拖拽连线,鼠标悬停在目标节点上时自动检测落点:

  • 悬停在节点 header 区域 → 创建节点级端口连线
  • 悬停在字段行 → 创建字段级端口连线

目标节点入口侧自动选择:源节点在目标左侧 → 从目标左侧进入;在右侧 → 从右侧进入(最优路径)。

端口坐标 API

import { getPortWorldPosition, getTargetPortWorldPosition } from 'pixi-graph-engine'

// 获取源端口世界坐标
const src = getPortWorldPosition(sourceNode, { nodeId: 'n1', portType: 'field', fieldKey: 'name' })

// 获取目标端口最优入口坐标(根据源 X 坐标自动选边)
const tgt = getTargetPortWorldPosition(targetNode, { nodeId: 'n2', portType: 'node' }, src.x)

Store API

全局响应式状态(基于 Vue reactive)。

import { store, addNode, removeNode, addEdge, setActiveNode, setSelected } from 'pixi-graph-engine'

// 读取
store.nodes          // Map<string, NodeModel>
store.edges          // Map<string, EdgeModel>
store.viewport       // { x, y, zoom }
store.activeNodeId   // string | null(当前进入编辑态的节点)
store.selectedNodeIds // Set<string>

// 操作
addNode(node)          // 添加节点到 store
removeNode('node-1')   // 从 store 移除节点
addEdge(edge)          // 添加边

setActiveNode('node-1')  // 打开编辑面板
setActiveNode(null)      // 关闭编辑面板

setSelected('node-1', true)   // 选中节点
setSelected('node-1', false)  // 取消选中

Layout Engine

布局引擎负责根据节点数据计算 Canvas 尺寸(layout.readonly / layout.edit)。
每次创建节点后必须调用 computeLayout 写入尺寸,否则节点无法正常渲染。

import { computeLayout, invalidateLayoutCache } from 'pixi-graph-engine'

// 创建时计算
node.layout = computeLayout(node)

// 数据变更后刷新(先清缓存再重算)
invalidateLayoutCache(node)
node.layout = computeLayout(node)

各类型默认尺寸参考:

| 类型 | 只读宽 | 只读高(示例) | |---|---|---| | object | 260px | header(40) + 字段行数×30px | | array | 240px | header(40) + 条目数×30px | | table | max(240, 列数×120)px | header(40) + 列头(28) + 数据行×26px | | json | 260px | 自适应(最大 400px) | | text | 自适应 | 自适应 |


Viewport 视口

视口采用「世界坐标 ↔ 屏幕坐标」两套体系,节点位置均为世界坐标。

import { worldToScreen, screenToWorld, applyViewportToPan, applyViewportZoom } from 'pixi-graph-engine'

// 坐标转换
const screen = worldToScreen(node.x, node.y)   // → { x, y }(屏幕 px)
const world  = screenToWorld(e.clientX, e.clientY)  // → { x, y }(世界坐标)

// 平移(通常在 pointermove 中调用)
applyViewportToPan(movementX, movementY)

// 缩放(deltaSign: 1 放大 / -1 缩小)
applyViewportZoom(deltaSign, centerX, centerY)

// 读取当前视口状态
const { x, y, zoom } = store.viewport

UI 组件

PixiGraphCanvas

全功能画布组件,内部集成所有子组件(Canvas、叠层、工具栏、MiniMap、连线交互等)。

<template>
  <PixiGraphCanvas />
</template>

该组件会自动接管 store 中的节点/边并完成渲染,使用前向 store 添加数据即可。


NodeEditor(编辑面板)

点击节点后弹出的编辑面板(通过 Overlay.vue 承载),支持对各字段进行增删改、类型切换。

触发方式:

// 编程式打开
setActiveNode('node-id')

// 通过 clickBehavior 配置自动触发(注册类型时设置)
clickBehavior: 'edit'

支持的字段编辑类型:

| FieldType | 编辑控件 | |---|---| | string | 单行文本输入 | | array | 可增删的列表项 | | object | JSON 格式 textarea | | table | JSON 格式 textarea({columns, rows}) | | json | JSON 格式 textarea | | custom | 多行文本 textarea |


MiniMap

小地图组件,实时同步画布内容与视口位置。

<template>
  <MiniMap ref="miniMapRef" />
</template>

位置与尺寸配置(运行时可动态修改):

import { setMinimapPosition, setMinimapSize, setMinimapVisible } from 'pixi-graph-engine'

setMinimapPosition('bottom-right')   // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
setMinimapSize(200, 150)             // width, height(px)
setMinimapVisible(false)             // 显示/隐藏

Toolbar

顶部工具栏,包含:选择工具 / 平移工具 / 框选。

<template>
  <Toolbar />
</template>

编程切换工具模式:

import { setTool } from 'pixi-graph-engine'

setTool('select')  // 选择(默认)
setTool('pan')     // 平移

// 读取当前工具
import { toolState } from 'pixi-graph-engine'
toolState.current  // 'select' | 'pan'

快捷键(内置):

| 按键 | 功能 | |---|---| | Space + 拖拽 | 临时平移 | | Esc | 取消连线 | | 鼠标中键/右键拖拽 | 平移画布 | | 滚轮 | 缩放 |


X6 迁移适配器

提供从 AntV X6 图数据直接迁移的工具函数。

import { fromX6Node, fromX6Edge, migrateGraph } from 'pixi-graph-engine'

// 单节点迁移
const node = fromX6Node(x6Node)

// 单边迁移
const edge = fromX6Edge(x6Edge)

// 整图批量迁移
const { nodes, edges } = migrateGraph(x6Graph)
nodes.forEach(n => {
  n.layout = computeLayout(n)
  addNode(n)
})
edges.forEach(e => addEdge(e))

完整初始化示例

// main.ts 或 App.vue onMounted

import {
  registerNodeType,
  createNode, createEdge,
  computeLayout,
  addNode, addEdge,
  createPixiApp, initScene,
  renderNode, renderEdge,
  rebuildIndex, initEventSystem,
  store
} from 'pixi-graph-engine'

// 1. 注册自定义节点类型(可选)
registerNodeType('data-source', {
  base: 'object',
  headerBackground: 0x0f766e,
  showStatus: true,
  editable: true,
  heightMode: 'compact',
  hoverToolbar: {
    position: 'top-end',
    actions: '<button onclick="alert(1)">刷新</button>'
  }
})

// 2. 初始化 PixiJS
const canvas = document.querySelector('canvas')!
const app = await createPixiApp(canvas)
const { nodes: nodesLayer, edges: edgesLayer } = initScene(app)

// 3. 创建节点和边
const n1 = createNode({
  id: 'n1', type: 'object', x: 100, y: 100,
  customType: 'data-source',
  data: {
    host: { type: 'string', value: 'db.example.com' },
    port: { type: 'string', value: '5432' }
  },
  status: 'success'
})
n1.layout = computeLayout(n1)
addNode(n1)

const n2 = createNode({
  id: 'n2', type: 'array', x: 450, y: 100,
  data: ['alpha', 'beta', 'gamma']
})
n2.layout = computeLayout(n2)
addNode(n2)

const e1 = createEdge({ id: 'e1', source: 'n1', target: 'n2' })
addEdge(e1)

// 4. 渲染所有节点和边
store.nodes.forEach(node => renderNode(node, nodesLayer))
store.edges.forEach(edge => renderEdge(edge, store.nodes, edgesLayer))

// 5. 构建空间索引 & 初始化交互
rebuildIndex(store.nodes)
const cleanup = initEventSystem(canvas)

// 组件卸载时清理
onUnmounted(() => cleanup())

注意事项

  1. computeLayout 必须在 addNode 前调用,否则节点尺寸为初始占位值 {240×68}
  2. 节点数据变更后(如编辑字段),需调用 invalidateLayoutCache(node) 清除缓存,然后重新 computeLayoutrefreshNode
  3. full 模式节点的高度由 HTML overlay 实际渲染后测量同步,初次渲染会有一次短暂的高度跳变(defaultHeight 为初始占位)。
  4. 字段级连线portType: 'field')仅支持 object / array / table 类型节点。
  5. 图标字体(如 Material Icons)需在项目中预先加载后再调用 setDefaultIconFont