三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

30分钟用maxGraph从零搭一个能拖拽的流程图编辑器

30分钟用maxGraph从零搭一个能拖拽的流程图编辑器

30分钟用maxGraph从零搭一个能拖拽的流程图编辑器

【免费下载链接】maxGraphmaxGraph is a fully client side JavaScript diagramming library项目地址: https://gitcode.com/gh_mirrors/ma/maxGraph

先别急着找"流程图生成网站"。想象一下这个场景:你刚给产品经理画完一版业务流程图,第二天需求变了三个节点、两条线,你只能重新画一遍——如果是静态图片,意味着推倒重来;如果手写 SVG 坐标,光是对齐就够喝一壶。而用 maxGraph,这个纯客户端的 TypeScript 图表库,你画出来的不是"一张图",而是一套能被代码驱动、能响应用户操作、能自动排版的图表应用。今天我就带你从零把它跑起来。

画图不难,难的是"能改、能存、能交互"

市面上画图方案其实不少,但大多数都卡在同一个地方:图是静态的

  • 用图片或 SVG 硬编码:每次改需求都要重新出图,节点位置全靠手算坐标,图一多直接心态爆炸;
  • 用在线画图工具:导出的图无法被程序读取,更别说让用户拖拽、缩放、连线;
  • 用商业图表库:功能是够了,但价格贵、闭源,想定制一个泳道样式都得看厂商脸色。

maxGraph 正好补上这个缺口。它管理着图表世界的三块基石:顶点(矩形、圆形乃至你自定义的形状)、(连接顶点的线、箭头)、分组(类似文件夹,把相关元素装在一起)。在这之上,它还能做布局算法、图论运算和深度的 API 控制。

它的身世也值得一提:2020 年老牌库 mxGraph 归档停更后,社区 fork 出 maxGraph 接棒维护,原生用 TypeScript 重写,同时保留了 mxGraph 的 XML 兼容性——这意味着老项目的数据文件还能接着用。

对于开发者的钱包和硬盘它也很友好:零第三方依赖,JS 代码符合 ES2020 规范,同时提供 CommonJS 和 ES Module 两种格式,支持 tree-shaking(按需打包),想在 React、Vue 还是原生 JS 里用都行。

第一个可运行的 demo:一个矩形、一个圆、一条线

先装包,这一步没什么悬念:

npm install @maxgraph/core

注意一个前提:maxGraph 需要配合构建工具(webpack、Vite 等)使用,不支持直接<script>标签引入,这也是后面避坑指南要细说的点。

在 HTML 里准备一个容器元素:

<div id="graph-container" style="width: 100%; height: 500px"></div>

然后写入口代码。下面这段就是官方入门示例的完整版,我把每行的意图都标了出来:

import {type CellStyle, Graph, InternalEvent} from '@maxgraph/core'; // 1. 拿到页面上的容器,图表会渲染进这个 div const container = document.getElementById('graph-container') as HTMLElement; // 2. 屏蔽浏览器自带的右键菜单,否则后面"拖拽平移"会和它打架 InternalEvent.disableContextMenu(container); // 3. 创建图表实例,一切从这里开始 const graph = new Graph(container); // 4. 开启平移:按住鼠标右键拖动即可移动画布 graph.setPanning(true); // 5. batchUpdate 把多次增删改"打包"成一次事务 // 好处有两个:撤销历史更干净、批量渲染更高效 graph.batchUpdate(() => { // 插入第一个顶点:一个 100x100 的矩形,位置在 (10, 10) const rectangle = graph.insertVertex({ position: [10, 10], size: [100, 100], value: '矩形节点', // 显示在节点上的文字 }); // 插入第二个顶点:一个橙色的圆形,位置 (350, 90) const circle = graph.insertVertex({ position: [350, 90], size: [50, 50], style: { fillColor: 'orange', shape: 'ellipse', // 形状换成椭圆 }, value: '圆形节点', }); // 用一条线把两个节点连起来 graph.insertEdge({ source: rectangle, target: circle, value: '连接线', style: { edgeStyle: 'orthogonalEdgeStyle', // 直角拐弯的连接线 rounded: true, // 拐角处加圆角 }, }); });

跑起来你会看到类似这样的效果——而且注意,这个图是"活的",你可以拖动节点、移动画布、选中元素:

到这里,一个最小可用图表就诞生了。insertVertexinsertEdge是你日后最常打交道的两个 API,前者负责"摆东西",后者负责"连东西"。

三个每天都会用到的核心能力

demo 能跑只是起点,真正让 maxGraph 值钱的是下面三件事。

样式定制:别让你的节点全都长一个样

默认节点是朴素的白底黑边。想让所有节点统一风格?改默认样式即可,一次改动全图生效:

// 必须在任何 insertVertex 之前执行 const vertexStyle = graph.getStylesheet().getDefaultVertexStyle(); vertexStyle.rounded = true; // 直角变圆角 vertexStyle.dashed = true; // 边框变虚线 vertexStyle.strokeWidth = 2; // 边框加粗到 2 像素 vertexStyle.strokeColor = 'green'; // 边框变绿

效果对比很直观——左边是默认样式,右边是加了圆角、虚线、绿色边框之后的节点:

而如果只想某个节点特殊一点,就在它自己的insertVertex配置里写style,就像 demo 里橙色圆形那样。maxGraph 单节点可配置的样式属性有 100 多个,从填充色、透明度到图标、形状,基本覆盖你见过的所有图表风格。

事件监听:让图表知道用户干了什么

图表是给人用的,就得能响应用户操作。addListener是通往交互世界的大门:

// 用户点击某个单元格时触发 graph.addListener(InternalEvent.CLICK, (_sender, evt) => { const cell = evt.getProperty('cell'); if (cell) { console.log('你点击了单元格:', cell); // 在这里可以做任何事:弹详情、高亮相关节点、打开编辑面板…… } }); // 用户移动单元格时触发 graph.addListener(InternalEvent.CELLS_MOVED, (_sender, evt) => { const cells = evt.getProperty('cells'); console.log(`本次共移动了 ${cells.length} 个单元格`); });

这种事件机制覆盖了点击、移动、新增、删除、样式变更等几乎所有图的变化,是后续做"点击节点弹出详情""拖拽完成自动保存"这类功能的基石。

布局算法:把位置交给代码去算

手摆节点,摆到 30 个就烦了。maxGraph 内置了层级、树状、圆形、力导向(organic)、泳道等多种布局算法,你需要做的只是"喂数据、喊开始":

import {HierarchicalLayout} from '@maxgraph/core'; // 1. 先按层级关系把节点和连线加进图里(坐标随便给,布局会重排) graph.batchUpdate(() => { // ...一堆 insertVertex / insertEdge... }); // 2. 创建层级布局实例(适合流程图、组织架构图) const layout = new HierarchicalLayout(graph); // 3. 一句 execute,所有节点自动对齐成层级树 layout.execute(graph.getDefaultParent());

配上泳道(swimlane)之后,复杂的业务流程也能排得清清楚楚:

如果你的图是"关系型"的,可以换用FastOrganicLayout(力导向布局,模拟物理引力把节点弹开),几行代码就能让乱成一团的网络图自动"舒展开"。

踩坑预警:新手最容易翻车的 4 个地方

先别急着复制代码,看看这几个坑,能帮你省下至少一晚上的调试时间。

坑一:直接<script>引入会报错。maxGraph 要求通过打包器使用。它给你的@maxgraph/core包自带 ESM 和 CommonJS 两种格式,配合 Vite、Webpack 都顺畅,但别指望在纯 HTML 里裸奔。项目里提供了packages/ts-examplepackages/js-example等现成工程模板,可以直接照着抄配置。

坑二:忘了禁用右键菜单。在容器上调用InternalEvent.disableContextMenu(container)几乎应该成为固定仪式。否则浏览器的原生右键菜单会跟 maxGraph 自己的交互抢地盘,尤其是开启平移、框选之后,体验会非常割裂。

坑三:所有修改要放进batchUpdate初学者容易一条条调用insertVertex,图能出来,但性能差、撤销历史乱。把一批操作塞进batchUpdate(() => {...})里,会被当成一次原子操作——一次渲染、一步撤销,这也是官方示例的标准姿势。

坑四:坐标是"相对父节点"的。一旦用了分组,子节点的position是相对于父容器算的,不是全局坐标。这个细节在画复杂嵌套结构时特别容易踩,改位置之前先想清楚它属于哪个层级。

进阶玩法:从"能看"到"能用"

demo 和样式只是热身。当你的图开始承载真实业务,下面两招会让它质变。

分组 + 折叠:复杂度管理。图一多,人的眼睛就处理不过来了。用groupCells把相关节点装进一个父节点,再配合折叠(collapse/expand)功能,可以把几十个节点"收"成一个图标,需要时再展开。maxGraph 还支持 drill-down(下钻)——双击某个分组,直接进入它的子图去编辑,就像在文件系统里层层打开文件夹:

XML 序列化:让图能存、能传。图的最终归宿往往是"保存下来、下次再打开"。maxGraph 沿用 mxGraph 的 XML 格式,持久化就是两行代码的事:

import {ModelXmlSerializer} from '@maxgraph/core'; // 保存:把整个图的数据模型导出成 XML 字符串 const xml = new ModelXmlSerializer(graph.getDataModel()).export(); // 加载:把 XML 字符串还原成一张完整的图 new ModelXmlSerializer(graph.getDataModel()).import(xml);

导出后的 XML 可以存到数据库、写进文件,或者发给后端做数据流转。这也是它适合做"业务数据可视化"而不是"一次性插图"的根本原因——图和数据是绑定的

哪些项目真正适合用 maxGraph

不是所有图表需求都值得引入一个库,但下面这几种场景,maxGraph 几乎是量身定做的:

  • 流程/工作流编辑器:BPM 建模、审批流设计器,泳道、分支、事件全支持;
  • 网络拓扑与架构图:设备连设备、服务调用服务,配合力导向布局一键排布;
  • 组织结构图:部门层级天然就是树,层级布局直接出图;
  • 数据血缘可视化:字段级的上下游依赖,靠边和分组就能表达清楚。

而且它和框架无关,无论你的主工程是 React、Vue、Angular 还是纯原生,都能接入。想看各种功能的真实效果,仓库里的packages/html/stories存放了整套 Storybook 演示;想从工程级案例起步,packages/ts-examplepackages/js-example分别展示了 TypeScript 和 JavaScript 的完整配置。深入 API 和概念,packages/website/docs下的 manual(手册)与 usage(用法)目录是现成的参考手册。

如果想动手改源码研究内部实现,可以把这个仓库克隆到本地跑一遍示例:git clone https://gitcode.com/gh_mirrors/ma/maxGraph

动起来:给你的下一步建议

回到开头的场景——如果你的流程图需求还在"重新画一遍"的循环里,现在有了解法。maxGraph 让你做的不是画图,而是构建一个能画图的程序:节点数据化、连线可编程、布局自动化,用户拖拽修改,程序负责兜底。

接下来怎么走?我的建议是三步:

  1. 把文中的 demo 跑通,亲手拖一拖节点,感受一下"活图"和"死图"的区别;
  2. 挑一个你真实遇到的场景(比如审批流、组织架构),用insertVertex+insertEdge把它数据化;
  3. 加上事件监听和 XML 保存,让图表和你的业务数据真正打通。

半小时后回头看,你会发现自己已经告别了"画图工具",迈进了"图表应用开发者"的行列。

【免费下载链接】maxGraphmaxGraph is a fully client side JavaScript diagramming library项目地址: https://gitcode.com/gh_mirrors/ma/maxGraph

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

← 返回列表