@ bruno00o/ 图表
框架可知性,用WebGL2提供打入的图形可视化.
- 包括5万个节点和10万个边缘的平滑
- 声明标记: 将您的字段映射到颜色、 大小、 标签、 箭头和破折叠度通过平面图共享或保存到一个标记, 从数据中推断类型
- 已键入事件和状态:单击、徘徊、拖放和焦点目标带有自己的项目类型,以标记名称收缩,无所可投
- 直播来源:通过 TanStack DB 收藏, 或任何与
subscribeChanges重新绘制 - 在 GPU 上解决命名状态( 选择、 徘徊、 焦点和您自己)
- 图像和图标被剪接到节点形状、角徽章、轮廓、标签背景、平行边缘和自环
- 边上有两端, 分别是颜色、 宽度、 破折叠和标签
- 嵌入组被绘制为容器,在双击上折叠成一个单一的节点,交叉的边缘被合并并被其计数所样式
- 标有自有大小、颜色、重量、字体、放入、换行和截取等标签,标有节点和边缘,边缘标签随其边缘并保持正上;标签只放在适合的位置而不重叠
- 拖放、平板、缩放、按住触摸屏、多选择和拉索出框外,加上键盘导航功能宣布用于屏幕阅读器
- 缩放、平板或离开页面滚动、缩放限制和限制视图的轮子不能离开,并拖动边滚动
- 在 Web Worker 中强制 GPU 的布局, 并带有 CPU 倒置: 节点在保持平稳时固定, 并拖取节点针
- 适应缩放的背景网格、环绕结点组、状态或列表的船体,以及HTML 被固定在节点、边缘或点上而无需每个框的渲染
- 带有 CSS 变量、 光和暗
- 导出到 PNG , 超过 GPU 限制, 或导出到一个独立的 SVG 文档
- 按经度和纬度布置的节点,在自己的WebGL上下文中被绘制在地图Libre地图内,并带有地图,图和归属的PNG导出
- 查看您的应用程序可以控制, 作为选择, 没有滞后框架
- 一个框架不可知核心,带有薄回放适配器,外加 TanStack DB 和 TanStack 查询集成
早期开发:API仍然可能在1.0之前改变.
安装
pnpm add @bruno00o/graph
| 入境点 | 目录 | 同伴依赖关系 |
|---|---|---|
@bruno00o/graph | defineGraph, (中文(简体) ), nodes, (中文(简体) ), edges, (中文(简体) ), hull, (中文(简体) ), mountGraph | |
@bruno00o/graph/react | <Graph>, (中文(简体) ), <Anchor>, (中文(简体) ), useGraph, (中文(简体) ), useGraphState, (中文(简体) ), useGraphEvent | react 19号, react-dom 第 19 条 |
@bruno00o/graph/db | persistPositions 财务报告和审定财务报表 subset 用于 TanStack DB 收藏 | @tanstack/db |
@bruno00o/graph/query | createExploration 用于装入 TanStack 查询的邻居 | @tanstack/query-core |
@bruno00o/graph/cypher | cypherGraph,节点和边缘来自一个 Cypher 查询的结果 | |
@bruno00o/graph/export | exportPng 财务报告和审定财务报表 exportSvg | |
@bruno00o/graph/map | mountMapGraph 财务报告和审定财务报表 exportMapPng 用于地图Libre地图 | maplibre-gl 6个 |
@bruno00o/graph/map/react | <MapGraph>,用于 MapLibre 地图的回放组件 | maplibre-gl 6, (简体中文), react 19号, react-dom 第 19 条 |
@bruno00o/graph/layouts | layers, (中文(简体) ), grid, (中文(简体) ), rings 财务报告和审定财务报表 radial 版式 | |
@bruno00o/graph/layouts/force | force 布局,在Web Worker的GPU上计算 | |
@bruno00o/graph/devtools | TanStack Devtools 的图形面板和渲染时间 | @tanstack/devtools |
@bruno00o/graph/devtools/react | graphDevtoolsPlugin 财务报告和审定财务报表 <GraphDevtoolsPanel> 用于反应 | @tanstack/devtools-utils, (中文(简体) ), react 第 19 条 |
@bruno00o/graph/theme.css | 默认光和暗主题 |
快开始
import { defineGraph, edges, mountGraph, nodes } from '@bruno00o/graph'
import '@bruno00o/graph/theme.css'
type Item = { id: string; label: string; group: string; weight: number }
type Link = { id: string; from: string; to: string }
const definition = defineGraph({
marks: [
edges<Link>(links, { source: 'from', target: 'to', arrow: 'target' }),
nodes<Item>(items, { color: 'group', size: 'weight', label: 'label' }),
],
states: { muted: { opacity: 0.2, label: false } },
})
const graph = mountGraph(container, { definition, ariaLabel: 'Sample graph' })
graph.on('click', ({ target }) => {
if (target?.kind === 'node') console.log(target.datum.label)
})
graph.states.set('muted', { nodes: ['n4', 'n7'] })
用反应 :
import { defineGraph, edges, nodes } from '@bruno00o/graph'
import { Graph, useGraphState } from '@bruno00o/graph/react'
import { useMemo } from 'react'
function Hovered() {
const hovered = useGraphState((state) => state.hovered)
return <output>{hovered?.kind === 'node' ? String(hovered.key) : ''}</output>
}
export function SampleGraph({ items, links }: { items: Item[]; links: Link[] }) {
const definition = useMemo(
() =>
defineGraph({
marks: [
edges(links, { source: 'from', target: 'to' }),
nodes(items, { color: 'group', label: 'label' }),
],
}),
[items, links],
)
return (
<Graph definition={definition} ariaLabel="Sample graph" style={{ height: 480 }}>
<Hovered />
</Graph>
)
}
无节点 x 财务报告和审定财务报表 y 按定义的布局排列。 它运行在第一个节点上,然后在邻居附近放置新的节点而不移动其他节点.
import { force } from '@bruno00o/graph/layouts/force'
const definition = defineGraph({ marks, layout: force({ edgeLength: 60 }) })
const graph = mountGraph(container, { definition, ariaLabel: 'Sample graph' })
button.addEventListener('click', async () => {
const completed = await graph.layout.run(undefined, { keys: graph.selection.get().nodes })
if (completed) graph.view.fit({ ...graph.selection.get(), duration: 400 })
})
GPU上运行的大图 (WebGL2 in the worker)和CPU上运行的小图; force({ engine: 'gpu' }) 或者说 'cpu' 强迫选择. graph.layout.run() 确定每个节点,或者只 keys,并决心 false 当停止。 进步在商店里生活state.layout.running 财务报告和审定财务报表 state.layout.progress),以及 layoutstart 财务报告和审定财务报表 layoutend 标记每次运行.
grid, (中文(简体) ), rings 财务报告和审定财务报表 radial 立即计算结果 节点滑翔过来 transitions.layout (默认为400毫秒)和 fit 他们降落的地方.
import { grid, radial } from '@bruno00o/graph/layouts'
await graph.layout.run(grid<Item>({ order: (a, b) => a.group.localeCompare(b.group) }), {
fit: true,
})
await graph.layout.run(radial({ root: 'hub' }), { duration: 600 })
一个 TanStack DB 收藏是一个活源:通过它到一个标记,它的更改在没有新定义的情况下到达.
nodes(itemsCollection, { color: 'group', label: 'label' })
要过滤一个大收藏, subset 用一个 where 表达式:一个新的过滤器只移动输入或离开的行,而点取的收藏只载出图表要求的.
import { subset } from '@bruno00o/graph/db'
import { gte } from '@tanstack/db'
const heavy = subset(itemsCollection, (item) => gte(item.weight, 40))
nodes(heavy, { color: 'group' })
实例
这个 examples 文件夹有可操作的示例:香草和回放图、图像、徽章和轮廓、平行边缘和回放、折叠和展开的嵌入组、有船体和锚定的HTML的背景网格、高达50万个节点、分层、网格、环和射线布局、与TanStack DB的分页间协作、过滤50万个TanStack DB行的边缘并按要求加载、与TanStack Query相邻的探索、以及绘制在地图上的地方。 跟着他们跑 pnpm install && pnpm dev。 。 。 .
参与
- 欢迎提出问题和拉动请求.
pnpm verify运行 lint, 类型, 测试, 构建和包检查 .- 添加
?caps=low以实例 URL 运行到最小的 WebGL2 设备:最小限制和没有可选扩展.?caps=software报告软件渲染器,以及&texture=4096仅降低最大纹理大小。 在测试中,limitWebgl从src/testing/webgl-caps.ts并列出设备拒绝的纹理. pnpm test:browsers构建示例和文档,然后用 Playwright 打开 Chromium, Firefox 和 WebKit 中的每一个示例页面. 每个页面必须记录不出错,绘制其画布,在悬浮上选择一个节点,在点击时选择并缩放在车轮上. 它按浏览器打印页面表,并保存每个页面的截图browser-results运行pnpm exec playwright install一次下载浏览器.--browser firefox或者说--browser chromium,webkit只运行一些浏览器 .--example nodes, (中文(简体) ),--example 'docs/*'或者说--example examples/labels只运行一些页面。 这个low/*页面打开五个示例?caps=low。 。 。 .--no-build重新使用最后的建筑,--software在没有GPU的机器上绘制其软件渲染器的铬,--headed显示窗口.- 在 Linux 上, Firefox 创建了仅包含显示的 WebGL 上下文: 运行
xvfb-run pnpm test:browsers在一台没有一台的机器上,像CI一样. - 每个例子页将其图表显示为
window.graph支票上写着.
添加实例
Docs 画廊读取所有文件夹 docs/src/examples,所以一个实例只触及到自己的文件夹:
docs/src/examples/<slug>/
meta.ts title, description, hint, category, order, tags, api, guides
main.ts exports mount(container), which returns the graph
main.tsx or exports a React Example that passes its ref to <Graph>
messages.ts every text the example shows
notes.mdx optional prose under the demo: how it works, what to try
thumbnail.webp generated, light theme
thumbnail-dark.webp generated, dark theme
pnpm docs:new-example <slug> <category>创建带有小图形的文件夹开始,--react以反应为例。 文件夹名称是弹簧和地址,/examples/<slug>:永远不要重命名一个已出版的.meta.ts导出对象satisfies ExampleMeta,并且只导入类型:title, (中文(简体) ),description(一两句,相与相与相接hint(什么尝试,在演示之上);category,类型之一docs/src/examples/categories.ts数字 :basics, (中文(简体) ),style, (中文(简体) ),layouts, (中文(简体) ),structure, (中文(简体) ),interaction, (中文(简体) ),analysis, (中文(简体) ),edges, (中文(简体) ),time-and-maps, (中文(简体) ),data, (中文(简体) ),tools· ;order(a) 在类别内,分10个步骤,使一个新的例子适合其他两个例子;tags数字 :Vanilla或者说React条目文件后加TanStack DB, (中文(简体) ),TanStack Query财务报告和审定财务报表TanStack Devtools当实例导入时;api,页/docs/api实例使用,例如define-graph,以及guides,页/docs例如states或者说view#view-limits: 与两者的页面链接;harness: { select: false }当点击一个节点时,不选择它.
- 代码查看器显示文件夹的每个文件, 然后显示它们从
../shared。页上列出的模块来自输入. pnpm docs:thumbnails <slug>构建道克,打开Chromium中的页面,等待第一个渲染和布局的结束,并用400个光和暗的WebP图像将舞台保存为两个640个,每个图像约25个KB. 没有一发子弹,它就能捕捉到每一个例子;--no-build复用后一积. 提交两个图像 .pnpm test在错误的元数据、 未知的类别、 重复的 switter 、 缺失的 docs 页面的链接、 与导入不匹配的标签、 丢失的缩略图、 文件夹外的导入,../shared以及公共入口,或脚手架上留下的文字。 礛pnpm test:browsers --example 'docs/<slug>'检查三个浏览器中的页面.pnpm docs:dev向5192号港口的文件提供服务;pnpm --dir docs exec vite dev --port <port>再选一个.
公约:
- 一个例子,一个想法,大约50到150行.
- 中性以固定的种子生成数据,这样每个运行和每个缩略图都是相同的:没有商业域,没有包含一个的活世界数据集.
../shared/data有种子的发电机. - 屏幕上的每个文字都来自示例
messages.ts。示例拉动请求不编辑../shared或文件夹外的任何文件. - 没有投篮
datum:打出标记推断出. - 只有公共入境点:
@bruno00o/graph, (中文(简体) ),@bruno00o/graph/react, (中文(简体) ),@bruno00o/graph/layouts以及其他的子路径.