
LogicFlow 节点体系完全指南从内置 SVG 基础节点到业务自定义节点【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow本篇技术指南聚焦 LogicFlow 的节点Node体系先认识框架内置的 7 种 SVG 基础节点及其数据模型再深入讲解基于继承与重写的自定义节点机制model数据层与view视图层并结合仓库源码揭示样式属性、形状属性、getShape渲染与锚点计算之间的底层关系。读完本文你将掌握定义符合自身业务语义的自定义节点、动态样式控制以及复杂 SVG 图形绘制的完整实战能力。前置阅读适合已经跑通过快速上手和实例与图数据的读者。本文不展开连接规则、锚点、React/Vue 节点方案这些内容请继续阅读进阶节点。相关 API 参考nodeModel、graphModel、主题。认识基础节点7 种 SVG 形状LogicFlow 是基于 SVG 实现的流程图编辑框架节点和连线都是 SVG 基本形状。因此对 LogicFlow 节点样式的修改本质上就是对 SVG 基本形状的修改。框架内部共内置 7 种基础节点序号节点底层 SVG 元素对应 view 实现对应 model 实现1矩形rectRectNode.tsxRectNodeModel.ts2圆形circleCircleNode.tsxCircleNodeModel.ts3椭圆ellipseEllipseNode.tsxEllipseNodeModel.ts4多边形polygonPolygonNode.tsxPolygonNodeModel.ts5菱形polygon由多边形派生DiamondNode.tsxDiamondNodeModel.ts6文本textTextNode.tsxTextNodeModel.ts7HTMLhtmlHtmlNode.tsxHtmlNodeModel.ts从源码结构看这 7 种节点的 model 全部继承自 BaseNodeModel.tsview 全部继承自 BaseNode.tsx最终在 view/node/index.ts 与 model/node/index.ts 中统一导出。基础节点的默认尺寸是宽 100、高 80见 BaseNodeModel.ts 中_width 100、_height 80并带有最小 30、最大 2000 的宽高约束。基础节点本身非常简单但业务中对节点外观的需求千变万化。LogicFlow 提供了非常强大的自定义节点能力支持开发者自定义各种节点。官方示例目录 sites/docs/src/tutorial/basic/node/shapes 中就有全部 7 种基础节点的可视化展示。自定义节点基于继承与重写LogicFlow 通过继承来实现自定义节点和边开发者继承内置节点然后利用面向对象机制重写其方法。注意LogicFlow 推荐在实际应用场景中所有节点都使用自定义节点将节点的type定义为符合项目业务意义的名称而不是使用圆形、矩形这种仅表示外观的节点。节点的model与viewmodel数据层包含节点各种样式边框、颜色、形状宽高、顶点位置、业务属性等。view视图层控制节点的最终渲染效果。通过改变model就可以满足大部分自定义需求同时可以在view上定制更复杂的 SVG 元素。LogicFlow 基于 MVVM 模式实现。在自定义一个节点时可以重新定义节点的model和view通过重写定义在model上获取样式相关的方法以及重写view上的getShape来定义复杂的节点外观。从源码看MVVM 的实现依托 preact 与 mobxmodel 中的属性大量使用observable装饰器如 BaseNodeModel.ts 中的x、y、width、height、isSelected等保证数据变化能自动驱动视图更新view 则以 preact 组件的形式渲染 SVG。如果熟悉 React 开发阅读 packages/core 源码的难度与阅读自己的项目相当。注册自定义节点使用lf.register。从 LogicFlow.tsx 的源码可以看到register支持两种方式方式一推荐传入{ type, view, model }配置对象方式二lf.register(type, fn)通过回调函数拿到{ RectNode, RectNodeModel }等基类后再返回{ view, model }适合极个别需要引用lf实例的场景。以logicflow/extension中提供的可缩放节点为例基础节点不支持缩放extension 包在基础节点之上封装了缩放逻辑后发布出去开发者可以直接基于 extension 中的可缩放节点进行二次自定义import { RectResize } from logicflow/extension; class CustomNodeModel extends RectResize.model {} class CustomNode extends RectResize.view {}自定义节点modelLogicFlow 把自定义节点外观分为自定义节点样式属性和自定义节点形状属性两种方式。更多详细定义方法请查看 NodeModel API。1. 样式属性重写getNodeStyle样式属性控制节点的边框、颜色这类偏外观的属性。这些属性可以直接通过主题配置控制自定义节点样式可以看作在主题的基础上、基于当前节点的类型再次定义。例如在主题中对所有rect节点定义边框颜色为红色stroke: red那么可以在自定义节点UserTask时重新定义其边框为蓝色class UserTaskModel extends RectNodeModel { getNodeStyle() { const style super.getNodeStyle(); style.stroke blue; return style; } }从源码看getNodeStyle的合并链在 RectNodeModel.ts 中体现得很清晰先取BaseNodeModel.getNodeStyle()返回的theme.baseNode与节点自身style再与主题中的rect配置、properties.style逐层合并——主题 节点自身属性 节点 data 中的 properties.style。2. 形状属性重写setAttributes/initNodeData形状属性指节点的宽width、高height矩形的圆角radius、圆形的半径r、多边形的顶点points等控制节点最终形状的属性。因为 LogicFlow 在计算节点锚点、连线起点终点时会基于形状属性计算所以形状属性的自定义必须在setAttributes方法或initNodeData方法中进行class customRectModel extends RectNodeModel { initNodeData(data) { super.initNodeData(data); this.width 200; this.height 80; this.radius 20; // 矩形特有 } // or setAttributes() { this.width 200; this.height 80; this.radius 20; // 矩形特有 } }这两个方法在 BaseNodeModel.ts 的构造函数中被依次调用且注释明确说明二者的区别initNodeData只在节点初始化时调用setAttributes除了初始化时调用外每次properties变化时也会触发。这也是为什么基于properties响应式更新形状需要写在setAttributes中。LogicFlow 对不同基础节点存在各自特有的形状属性矩形radius圆角默认 0见 RectNodeModel.ts圆形r半径椭圆rx、ry多边形points顶点数组。默认points就是菱形[[50,0],[100,50],[50,100],[0,50]]见 PolygonNodeModel.ts并会在设置width/height时通过normalizePolygon归一化顶点坐标菱形rx、ry默认 30 / 50见 DiamondNodeModel.ts。详情见 NodeModel API 形状属性。注意如果不在model中设置形状属性而是直接在view中定义生成图形的宽高等形状属性会出现锚点位置、outline 大小不正确的情况连线的位置也可能出现错乱。因为锚点、外边框、连线端点都是基于model中的width和height生成的。3. 基于properties属性自定义节点样式在图数据中曾提到不论是节点还是边LogicFlow 都保留了properties字段它既可以用于修改元素的样式、形状属性也用于存放开发者的业务属性。因此可以在自定义节点时基于 properties 数据属性控制节点显示不同样式。下面这个例子展示了业务属性statu驱动边框颜色的完整实现源码见 properties/customRect.tsimport { RectNode, RectNodeModel } from logicflow/core; class CustomRectModel extends RectNodeModel { getNodeStyle() { const style super.getNodeStyle(); const properties this.properties; if (properties.statu pass) { style.stroke green; // 业务属性 statu 为 pass 时展示绿色边框 } else if (properties.statu reject) { style.stroke red; // 业务属性 statu 为 reject 时展示红色边框 } else { style.stroke rgb(24, 125, 255); } return style; } } class CustomRectNode extends RectNode {} export default { type: custom-rect, view: CustomRectNode, model: CustomRectModel, };对应的图数据见 properties/data.ts把业务属性、形状属性、样式属性都放在properties中{ id: 2, type: custom-rect, x: 300, y: 100, text: pass, properties: { statu: pass, // 业务属性 width: 100, // 形状属性 height: 100, radius: 20, style: { // 样式属性 strokeWidth: 3, }, }, },提示如果发现this.properties打印出来是一个 Proxy 对象、无法直接看到属性这是 mobx 的响应式代理所致可以使用{ ...this.properties }展开后打印。自定义节点view重写getShape自定义节点的model可以定义基础形状、样式等属性但当需要一个更复杂的节点时可以使用自定义节点view的方式重写getShape方法。getShape的作用是定义最终渲染的图形LogicFlow 内部会将其返回的内容插入到 SVG DOM 上。开发者不是一定要重写此方法只有在期望改变最终渲染图形 SVG DOM 时才需要使用它。例如rect节点最终渲染的 SVG DOM 只是一个矩形但想在上面加一个图标时就必须修改最终渲染图形的 SVG DOM这时就需要重写getShape。h渲染函数getShape的返回值会用到h函数。h是 LogicFlow 对外暴露的渲染函数其用法与 React、Vue 的createElement一致。这里需要创建的是 SVG 标签所以需要一定的 SVG 基础知识。基本语法为h(nodeName, attributes, [...children])举几个例子h(nodeName, attributes, [...children]) // text x100 y100文本内容/text h(text, { x: 100, y: 100 }, [文本内容]) /** * g * rect x100 y100 stroke#000000 strokeDasharray3 3/rect * text x100 y100文本内容/text * /g */ h(g, {}, [ h(rect, { x: 100, y: 100, stroke: #000000, strokeDasharray: 3 3}), h(text, { x: 100, y: 100 }, [文本内容]) ])一个结合model与view的完整示例位于 sites/docs/src/tutorial/basic/node/custom-view其中 userTask.ts 通过getShape在矩形内叠加了一个用户图标。节点外观的三种定义方式与优先级LogicFlow 定义一个节点的外观有三种方式优先级为主题 自定义节点 model 自定义节点 view主题定义所有此基础类型节点的通用样式例如定义所有rect节点的边框颜色、宽度等自定义节点 model定义此注册类型节点的数据存储和管理该节点样式、形状、业务等数据属性信息自定义节点 view定义此注册类型节点的 SVG DOM基于model的属性将数据渲染成用户可见的图形化形式。注意虽然自定义节点view优先级最高、功能也最完善理论上可以完全通过它实现任何想要的效果但此方式仍存在限制view最终生成的图形的形状属性必须和model中的形状属性一致因为节点的锚点、外边框都是基于model中的width和height生成view最终生成的图形整体轮廓必须和继承的基础图形一致不能继承rect却在getShape时返回circle轮廓因为 LogicFlow 对节点上的连线调整、锚点生成会基于基础图形进行计算。深入思考基础形状的坐标语义1. 为什么rect的x、y不是直接从model中获取在 LogicFlow 所有基础节点中model里的x、y统一表示元素中心点坐标。但getShape直接生成 SVG DOM 时不同元素类型对x、y的语义不同rectx、y表示图形左上角坐标所以一般要用中心点减去宽高的一半来计算左上角坐标const { x, y, width, height, radius } this.props.model // svg dom rect x100 y100 width100 height80 h(rect, { ...style, x: x - width / 2, y: y - height / 2, rx: radius, // 注意这里是 rx 属性而不是 radius ry: radius, width, height, })这一转换在内置 Rect.tsx 中同样存在leftTopX x - width / 2、leftTopY y - height / 2并在设置radius时自动映射为 SVG 的rx/ry见 Rect.tsx。自定义矩形的 view 时 radius 设置在model中radius是矩形节点的形状属性。但 SVG 中设置矩形圆角使用的是rx、ry而非radius所以自定义view的矩形时需要把model中radius的值赋给rx和ry否则圆角不生效。circle和ellipse通过cx、cy表示位置含义就是中心点坐标无需转换const { x, y, r } this.props.model; // svg dom circle cx100 cy100 r20 h(circle, { ...style, r, // 半径保持不变 cx: x, cy: y, })// 椭圆 const { x, y, rx, ry } this.props.model // svg dom ellipse cx100 cy100 rx20 ry10 h(ellipse, { ...style, cx: x, cy: y, rx, ry, })polygon所有顶点坐标已包含位置信息需要把model的points相对坐标加上中心点x、yconst { x, y, points } this.props.model const pointStr points.map((point) { return ${point[0] x}, ${point[1] y} }).join( ) // svg dom polygon points100,10 250,150 200,110 h(polygon, { ...style, points: pointStr, })2.props怎么用LogicFlow 基于 preact 开发自定义节点view时可以通过this.props获取父组件传递的数据。this.props对象包含两个属性model当前节点的 modelgraphModel整个 LogicFlow 图的 modelAPI 见 graphModel。3. 图标的 path 如何获取一般情况下图标可以找 UI 或从图标平台获取一个 SVG 格式文件然后用 IDE 以文本方式打开并格式化即可看到代码。代码中一般是最外层一个svg标签内部是一个或多个path此时用前面提到的h方法复现即可。SVG 标签一般包括如下属性viewBox允许指定一组图形伸展以适应特定容器元素一般直接把 SVG 标签上的viewBox属性值复制过来即可width和height不需要使用 SVG 文件里的原始值直接写成期望的宽高即可。path标签属性d定义路径的数据直接复制 SVG 代码即可无需关心d具体内容的含义fill路径的填充颜色一般和节点的边框颜色一致也可以按业务需求自定义。userTask.ts 中的getLabelShape就是标准做法外层svg设置viewBox: 0 0 1274 1024与期望的宽高内部两个path分别设置fill: style.stroke跟随节点边框色与从图标文件复制的d数据。源码级实战一个完整的业务节点将以上知识点串起来参考 userTask.ts 的完整UserTask节点可以看到业务节点开发中常重写的全部钩子方法import { RectNode, RectNodeModel, h } from logicflow/core; class UserTaskView extends RectNode { getShape() { const { model } this.props; const { x, y, width, height, radius } model; const style model.getNodeStyle(); return h(g, {}, [ h(rect, { ...style, x: x - width / 2, // 矩形默认 x/y 是左上角顶点这里切换为中心点 y: y - height / 2, rx: radius, ry: radius, width, height, }), this.getLabelShape(), // 自定义叠加的图标 ]); } } class UserTaskModel extends RectNodeModel { setAttributes() { const { scale 1, width 100, height 80 } this.properties; this.width width * scale; // 手动设置形状属性 this.height height * scale; } getTextStyle() { /* 依赖业务属性 isClicked 改变文本颜色 */ } getNodeStyle() { /* 依赖业务属性 isClicked 改变边框颜色 */ } getAnchorStyle() { /* 自定义锚点样式 */ } getAnchorLineStyle() { /* 自定义锚点拖出连线样式 */ } getOutlineStyle() { /* 自定义选中轮廓框样式 */ } } export default { type: UserTask, view: UserTaskView, model: UserTaskModel, };这个例子完整覆盖了本文的四个主题setAttributes中定义形状属性、getNodeStyle/getTextStyle中定义样式属性、基于properties的业务状态驱动、getShape中定义复杂 SVG DOM同时它还重写了getAnchorStyle、getAnchorLineStyle、getOutlineStyle来分别定制锚点、拖出连线和选中轮廓——这些钩子方法均定义于 BaseNodeModel.ts统一标注overridable支持重写。小结与后续本文从内置的 7 种 SVG 基础节点出发完整介绍了基于继承的自定义节点机制model层负责管理样式、形状与业务数据通过getNodeStyle、setAttributes/initNodeData、properties三个入口view层通过getShape与h函数定义最终渲染的 SVG DOM三者优先级为主题 自定义节点 model 自定义节点 view。掌握这些内容后即可根据业务需要创建任意外观的业务节点。更复杂的连接规则、锚点、框架节点以及 React/Vue 节点方案请继续阅读进阶节点React 节点、Vue 节点。相关 API 细节可查阅 nodeModel 与 register。【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考