LogicFlow nodeModel 完全指南:节点数据模型、属性分类与自定义节点核心 API
【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架,支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow
本文围绕 LogicFlow 的运行时 Model 层核心概念nodeModel展开,系统梳理节点的数据属性、状态属性、形状属性、其它属性四大分类,并逐一讲解initNodeData、setAttributes、getNodeStyle、连线/移动规则等可重写方法与常用实例方法的签名、用途与真实源码实现。读完本文,你将掌握"节点的一切操作本质都是对 model 的操作"这一数据驱动思想,并能基于 nodeModel 定制任意业务节点。
为什么需要理解 nodeModel
LogicFlow 中所有节点都会有一个nodeModel与其对应。由于框架采用数据驱动视图的机制,我们对节点的所有操作,事实上都是对 model 的操作:拖拽、选中、连边、修改文本、修改样式,最终都会落到nodeModel的某个属性或方法上。
因此,理解 nodeModel 是自定义节点、编写插件、做图数据持久化的前提。官方在文档中特别强调了一个重要原则:
大多数情况下,我们不建议直接对 nodeModel 的属性进行赋值操作,而是调用 model 或者 graphModel 上提供的方法。
:::error{title=警告} 在对 LogicFlow 内部源码不熟悉的情况下,对 model 的属性进行赋值操作可能会引起很多不符合预期的问题。例如在 model 中x、y表示节点的位置,如果想要移动节点直接修改x、y的话,会出现节点被移动了,而节点上的文本、节点相连的边都没有动。所以想要移动节点,最好的方法还是调用graphModel上的moveNode方法来实现。 :::
这一警告在源码中有直接印证。GraphModel.ts 中moveNode的实现并不只是修改坐标:它先通过nodeModel.getMoveDistance(deltaX, deltaY, isIgnoreRule)计算实际可移动距离(会执行移动规则校验、同步移动节点文本),再调用this.moveEdge(nodeId, deltaX, deltaY)让与该节点相连的所有边同步移动:
// 1) 移动节点 const node = this.nodesMap[nodeId] const nodeModel = node.model ;[deltaX, deltaY] = nodeModel.getMoveDistance(deltaX, deltaY, isIgnoreRule) // 2) 移动边 this.moveEdge(nodeId, deltaX, deltaY)这解释了为什么"直接改 x/y"会让文本与边"掉队"——正确做法永远是走 model/graphModel 提供的封装方法。
nodeModel 上的属性很多,按用途可以分为四类:数据属性、状态属性、形状属性、其它属性。
数据属性:持久化的核心字段
节点的数据属性是 LogicFlow 图数据中用于标识节点的数据。在流程图保存时,一般只保存节点的数据属性。
| 名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| id | string | ✅ | 节点 id |
| type | string | ✅ | 节点类型 |
| x | number | ✅ | 节点中心 x 轴坐标 |
| y | number | ✅ | 节点中心 y 轴坐标 |
| text | TextObject/string | 节点文本 | |
| properties | Object | 包含节点样式、形状属性和业务自定义属性 |
其中text以TextObject形式存在时,其结构如下:
| 名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| value | string | 文本内容 | |
| x | number | ✅ | 文本中心 x 轴坐标 |
| y | number | ✅ | 文本中心 y 轴坐标 |
| draggable | boolean | ✅ | 文本是否允许被拖动调整位置,保存时不会保存此属性 |
| editable | boolean | ✅ | 文本是否允许被双击编辑,保存时不会保存此属性 |
可以看到,draggable与editable属于运行时交互配置,保存时会被剔除。对应地,在 BaseNodeModel.ts 的formatText中,LogicFlow 会从graphModel.editConfigModel读取全局的nodeTextDraggable、nodeTextEdit作为文本默认的拖拽/编辑能力,再与传入的text合并,最终把标准化的TextConfig写回data.text。
从源码看,getData()(BaseNodeModel.ts)就是"保存时返回的数据"的最终形态:它输出{ id, type, x, y, properties },并且仅在存在文本value时才带上text,仅在开启INCREASE/STATIC堆叠模式时才带上zIndex,有旋转时带上rotate。
状态属性:细粒度样式与交互判断
状态属性一般用于自定义节点的时候,基于状态属性进行更细粒度的样式显示。
| 名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| isSelected | boolean | ✅ | 节点是否被选中 |
| isHovered | boolean | ✅ | 节点是否在 hover 状态 |
| isHitable | boolean | ✅ | 节点是否可点击 |
| draggable | boolean | ✅ | 节点是否可拖动 |
| isShowAnchor | boolean | ✅ | 是否显示锚点 |
| isDragging | boolean | ✅ | 是否在拖动 |
| visible | boolean | ✅ | 是否显示,1.1.0新增 |
在 BaseNodeModel.ts 中可以看到这些状态属性的默认值:
@observable isSelected = false @observable isHovered = false @observable isShowAnchor = false @observable isDragging = false @observable isHitable = true @observable isHittable = true // 细粒度控制节点是否对用户操作进行反应 @observable draggable = true @observable visible = true这里值得注意的细节:源码中同时存在isHitable与isHittable两个字段——注释明确说明isHitable是为了兼容历史拼写错误而保留的(BaseModel.ts)。此外,setHovered在置位 hover 的同时会自动联动setIsShowAnchor(见 BaseNodeModel.ts),即"悬停节点自动显示锚点"。
形状属性:决定基础节点外观
LogicFlow 的形状属性主要控制基础节点的主要外观。形状属性可以通过setAttributes或者initNodeData来设置。具体设置方式见自定义节点的形状属性。
| 名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| width | number | ✅ | 节点的宽度 |
| height | number | ✅ | 节点的高度 |
| radius | number | 矩形节点特有,节点的圆角 | |
| r | number | 圆形节点特有,圆的半径。对于圆形节点,会自动基于半径计算出节点的高度和宽度 | |
| rx | number | 椭圆节点和菱形节点存在,水平圆角的半径。会自动基于半径计算出节点的宽度 | |
| ry | number | 椭圆节点和菱形节点存在,垂直圆角的半径。会自动基于半径计算出节点的高度 | |
| points | [number,number][] | 多边形节点特有,多边形顶点。会自动基于顶点计算出节点的宽度和高度 |
不同形状节点的属性计算在源码中有明确实现:
- 圆形节点CircleNodeModel.ts:
width/height是r * 2的计算属性,因此只需维护r; - 椭圆节点EllipseNodeModel.ts:
width = rx * 2、height = ry * 2; - 多边形节点PolygonNodeModel.ts:
width/height由points中横纵坐标的极差计算得出;setAttributes会通过normalizePolygon(nextPoints, width, height)对顶点做归一化处理; - 矩形节点RectNodeModel.ts:
width/height/radius会从properties中读取并同步。
其它属性:框架信息与扩展挂载点
LogicFlow 在 model 上还维护一些属性,开发者可以通过这些属性拿到一些信息,例如拿到graphModel、节点的基础 model 类型等。
| 名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| graphModel | GraphModel | ✅ | 整个画布对应的 model,详情见 graphModelApi |
| zIndex | number | ✅ | 节点在 z 轴的高度,元素重合时,zIndex 高的在上面,默认为 1 |
| state | number | ✅ | 元素状态,不同的状态对应着元素显示效果。DEFAULT = 1 默认显示;TEXT_EDIT = 2 此元素正在进行文本编辑;ALLOW_CONNECT = 4,此元素允许作为当前边的目标节点;NOT_ALLOW_CONNECT = 5,此元素不允许作为当前边的目标节点 |
| BaseType | string | ✅ | 当前 model 的基础类型,对于节点,则固定为node。主要用在节点和边混合的时候识别此 model 是节点还是边 |
| modelType | string | ✅ | 当前 model 的类型,可取值有node,rect-node,circle-node,polygon-node,ellipse-node,diamond-node,html-node,text-node |
| moveRules | Model.NodeMoveRule[] | 节点被移动之前的校验规则 | |
| sourceRules | Model.ConnectRule[] | 节点连接其它节点时的校验规则 | |
| targetRules | Model.ConnectRule[] | 节点被其它节点连接时的校验规则 | |
| autoToFront | boolean | ✅ | 控制节点选中时是否自动置顶,默认为 true |
| incoming | object | ✅ | 进入当前节点的所有边和节点,v1.1.4 |
| outgoing | object | ✅ | 离开当前节点的所有边和节点,v1.1.4 |
| virtual | boolean | - | 是否为虚拟节点,默认 false。当为 true 时导出数据不会包含此元素。v1.1.24 |
关于state枚举,源码 constant/index.ts 中的定义为:
export enum ElementState { DEFAULT = 1, // 默认显示 TEXT_EDIT, // 此元素正在进行文本编辑 SHOW_MENU, // 显示菜单,废弃请使用菜单插件 ALLOW_CONNECT, // 此元素允许作为当前边的目标节点 NOT_ALLOW_CONNECT, // 此元素不允许作为当前边的目标节点 }modelType的全部取值在 constant/index.ts 中枚举,除了文档列出的节点类型外,还有custom-html-node以及边、图相关的类型。
:::info{title=modelType 与 type 的区别是什么?} 在自定义节点的时候,type可以是开发者自定义的任何值,但是在 LogicFlow 内部,涉及到这个节点的计算时,我们需要感知到这个节点的具体形状,这个时候不能用type,而是要用modelType来判断。 :::
这一区分在 BaseModel.ts 的源码注释中有更直白的表述:type更多是业务上的类型(例如 BPMN 场景中的bpmn:start-event),而modelType是外观上的类型——BPMN 的开始节点和结束节点type分别为bpmn:start-event和bpmn:end-event,但它们的modelType都是circle-node,因为外观都是基于圆形自定义而来。
incoming/outgoing的实现依赖graphModel的查询方法(BaseNodeModel.ts):
@computed get incoming(): GraphElements { return { nodes: this.graphModel.getNodeIncomingNode(this.id), edges: this.graphModel.getNodeIncomingEdge(this.id), } }样式属性:SVG 渲染与样式方法重写
LogicFlow 所有的节点最终都是以SVG DOM的方式渲染。但是除了形状属性之外,所有其它属于 SVG 的属性都不会直接存在nodeModel上。当开发者想要对 SVG DOM 添加更多的 svg 属性时,可以通过重写nodeModel上获取节点样式属性的方法来实现(stroke、fill、strokeDasharray等 SVG 属性)。
这类方法默认返回主题(Theme 类型说明)中的对应配置,全部支持重写:
getNodeStyle():自定义节点样式;getTextStyle():自定义节点文本样式;getAnchorStyle(anchorInfo):自定义节点锚点样式;getAnchorLineStyle(anchorInfo):自定义节点锚点拖出连接线的样式;getOutlineStyle():自定义节点轮廓框(选中外框)的样式;getRotateControlStyle():获取当前节点旋转控制点的样式。
例如,将节点描边改为蓝色并加虚线:
class UserTaskModel extends RectNodeModel { getNodeStyle() { const style = super.getNodeStyle(); style.stroke = "blue"; style.strokeDasharray = "3 3"; return style; } }自定义锚点样式与 hover 效果:
class UserTaskModel extends RectNodeModel { getAnchorStyle(anchorInfo) { const style = super.getAnchorStyle(anchorInfo); style.stroke = "rgb(24, 125, 255)"; style.r = 3; style.hover.r = 8; style.hover.fill = "rgb(24, 125, 255)"; style.hover.stroke = "rgb(24, 125, 255)"; return style; } }这些方法在源码中的默认实现都遵循"合并主题 + 深拷贝防止污染全局主题"的模式,例如 BaseNodeModel.ts:
getNodeStyle(): CommonTheme { return { ...this.graphModel.theme.baseNode, ...this.style, } }同时,各形状节点会在自身getNodeStyle中继续叠加形状主题与properties.style(如 RectNodeModel.ts),形成baseNode → 形状主题 → 节点 properties.style → 运行时 setStyle的优先级链条。
生命周期方法:initNodeData 与 setAttributes
initNodeData
支持重写,初始化节点数据,将传入的图数据(data)转换为节点属性,所以需要调用super.initNodeData触发转换方法。
- 在
super.initNodeData之前,对图数据进行处理; - 在
super.initNodeData之后,对节点属性进行初始化。
class UserTaskModel extends RectResize.model { initNodeData(data) { // 可以在super之前,强制设置节点文本位置不居中,而且在节点下面 if (!data.text || typeof data.text === "string") { data.text = { value: data.text || "", x: data.x, y: data.y + 40, }; } super.initNodeData(data); this.width = 100; this.height = 80; } }源码中initNodeData做了几件关键事情(BaseNodeModel.ts):补全空properties;按照"自定义createId> 全局idGenerator> 内置createUuid"的优先级生成 id;调用formatText标准化文本;将图数据通过assign(this, pickNodeConfig(data))合并进 model;并根据overlapMode初始化 zIndex。
:::info{title=提示} initNodeData 和 setAttributes 都可以对 nodeModel 的属性进行赋值,但是两者的区别在于:
initNodeData只在节点初始化的时候调用,用于初始化节点的属性。setAttributes除了初始化调用外,还会在 properties 发生变化时调用。 :::
以上面代码为例,由于节点缩放的时候会更新 properties 中的缩放后大小,也就会触发setAttributes。如果在setAttributes中定义节点的初始大小的话,会导致节点无法缩放。
setAttributes
设置 model 形状属性,每次 properties 发生变化会触发。
class UserTaskModel extends RectNodeModel { setAttributes() { const size = this.properties.scale || 1; this.width = 100 * size; this.height = 80 * size; } }从源码看,setAttributes的触发链路为:setProperty/setProperties/deleteProperty都会进入updateProperties,在更新this.properties后立即调用this.setAttributes(),并触发node:properties-change事件(BaseNodeModel.ts):
private updateProperties(nextProperties: P, updateKeys: string[]): void { const preProperties = toJS(this.properties) this.properties = nextProperties this.setAttributes() // 触发更新节点 node:properties-change 的事件 this.graphModel.eventCenter.emit(EventType.NODE_PROPERTIES_CHANGE, { id: this.id, keys: updateKeys, preProperties, properties: nextProperties, }) }因此setAttributes是实现"properties 驱动形状"的标准入口,例如根据业务属性联动节点尺寸、颜色等。
id 生成与文本格式化
createId
支持重写,自定义节点 id 的生成规则。
:::warning{title=注意}
- 请保证此方法返回 id 的唯一性。
- 此方法为同步方法,如果想要异步修改节点 id,请参考相关 issues(didi/LogicFlow#272)。 :::
import { v4 as uuidv4 } from "uuid"; class UserTaskModel extends RectNodeModel { createId() { return uuidv4(); } }源码中 BaseModel.ts 的注释同样强调:此方法必须是同步方法,如果想要异步修改 ID,建议删除此节点后在同一位置创建一个新的节点。
formatText
初始化文本,参数为data: LogicFlow.NodeConfig。
class UserTaskModel extends RectNodeModel { formatText(data: LogicFlow.NodeConfig) { console.log('data', data); const defaultText = { value: '', x: data.x, y: data.y, draggable: false, editable: true, } if (!data.text) { data.text = { ...defaultText } } else { if (typeof data.text === 'string') { data.text = { ...defaultText, value: data.text, } } // ... } } }数据读取:getData 与 getProperties
getData
获取被保存时返回的数据。LogicFlow 有固定的节点数据格式。如果期望在保存数据上添加数据,请添加到properties上。不支持重写此方法(源码中getData会被resize、历史记录等内部逻辑复用,重写会破坏数据契约)。
const nodeModel = lf.getNodeModelById("node_1"); const nodeData = nodeModel.getData();getProperties
获取节点属性(返回深拷贝,避免外部直接修改内部响应式对象)。不支持重写此方法。
const nodeModel = lf.getNodeModelById("node_1"); const properties = nodeModel.getProperties();锚点体系:getDefaultAnchor 与锚点属性
getDefaultAnchor
重新设置默认锚点,可以给锚点加上 id 等自定义属性,用于对锚点的验证。
class cNode extends RectNodeModel { // 定义节点只有左右两个锚点. 锚点位置通过中心点和宽度算出来。 getDefaultAnchor() { const { width, height, x, y, id } = this; return [ { x: x - width / 2, y, name: 'left', id: `${id}_0` }, { x: x + width / 2, y, name: 'right', id: `${id}_1`, edgeAddable: false }, ] } }锚点属性:
| 名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| x | number | ✅ | 锚点 x 坐标 |
| y | number | ✅ | 锚点 y 坐标 |
| id | string | ✅ | 锚点 id |
| edgeAddable | boolean | ✅ | 是否允许此锚点手动创建连线,默认为 true |
从源码看,锚点体系比文档展示的更完整:getAnchorsByOffset()(BaseNodeModel.ts)优先使用anchorsOffset(偏移量锚点),没有时才回退到getDefaultAnchor();而anchorsgetter 还会把所有锚点按节点rotate旋转角做矩阵变换(Matrix),保证旋转后锚点坐标仍然正确(BaseNodeModel.ts)。
相关配套方法:
getTargetAnchor(position: LogicFlow.Point):获取目标锚点,返回值BaseNodeModel.AnchorInfo。默认实现是取离鼠标位置最近的锚点getClosestAnchor,也支持通过graphModel.customTargetAnchor全局覆盖(BaseNodeModel.ts);getAnchorInfo(anchorId?: string):根据锚点 id 查询锚点信息;anchors:获取锚点,返回值LogicFlow.Point[],即const { anchors } = node。
连线与移动规则:业务约束的核心手段
getConnectedSourceRules / getConnectedTargetRules
getConnectedSourceRules:获取当前节点作为边的起始节点规则。支持重写,重写时可以自定义添加额外规则。所有规则中,有任一规则不满足,则禁止连线。getConnectedTargetRules:获取当前节点作为边的目标节点规则。支持重写,重写时可以自定义添加额外规则。所有规则中,有任一规则不满足,则禁止连线。
class EndNodeModel extends CircleNodeModel { getConnectedSourceRules(): ConnectRule[] { const rules = super.getConnectedSourceRules(); const geteWayOnlyAsTarget = { message: "结束节点只能连入,不能连出!", validate: ( source: BaseNodeModel, target: BaseNodeModel, sourceAnchor, targetAnchor ) => { let isValid = true; if (source) { isValid = false; } return isValid; }, }; rules.push(geteWayOnlyAsTarget); return rules; } }class StartEventModel extends CircleNodeModel { getConnectedTargetRules() { const rules = super.getConnectedTargetRules(); const notAsTarget = { message: "起始节点不能作为边的终点", validate: () => false, }; rules.push(notAsTarget); return rules; } }源码层面,规则校验的实际入口是isAllowConnectedAsSource/isAllowConnectedAsTarget(BaseNodeModel.ts):两者都返回{ isAllPass, msg }结构(即LogicFlow.ConnectRuleResult),遍历规则数组并短路——任一规则validate返回 falsy 即中止并返回该规则的message。ConnectRule的类型定义在 BaseModel.ts 中,其validate签名还会在开启adjustEdgeStartAndEnd后传入调整边的edgeID。
动态分组插件的实战佐证:在 dynamic-group/model.ts 中,分组节点通过重写getConnectedTargetRules/getConnectedSourceRules追加"分组节点不允许作为边的终点/起点"规则,并通过getDefaultAnchor在禁止手动连线时把锚点的edgeAddable置为false——这正是本文所讲三个可重写方法的组合应用,也是验证规则 API 的正确用法。
isAllowConnectedAsSource / isAllowConnectedAsTarget
isAllowConnectedAsSource:在连接边时,是否允许这个节点为 source 节点,边到 target 节点。
参数:
| 名称 | 类型 | 是否必填 | 描述 | | ------| -------------- | --- | -- | | target |BaseNodeModel| ✅ | 目标节点 | | sourceAnchor |Model.AnchorConfig| ✅ | 源锚点 | | targetAnchor |Model.AnchorConfig| ✅ | 目标锚点 | | edgeId |string| - | 调整后边的 id |
返回值LogicFlow.ConnectRuleResult(详见 didi/LogicFlow#926)。
isAllowConnectedAsTarget:在连线时,判断是否允许这个节点为 target 节点。
参数:
| 名称 | 类型 | 是否必填 | 描述 | | ------| -------------- | --- | -- | | source |BaseNodeModel| ✅ | 源节点 | | sourceAnchor |Model.AnchorConfig| ✅ | 源锚点 | | targetAnchor |Model.AnchorConfig| ✅ | 目标锚点 | | edgeId |string| - | 调整后边的 id |
返回值LogicFlow.ConnectRuleResult(详见 didi/LogicFlow#926)。
isAllowMoveNode
是否允许移动节点。参数deltaX: number, deltaY: number,返回值boolean | Model.IsAllowMove。其中IsAllowMove的结构为{ x: boolean; y: boolean }(BaseModel.ts),支持分别限制 X / Y 方向的移动。
class UserTaskModel extends RectNodeModel { isAllowMoveNode(deltaX: number, deltaY: number) { let isAllowMoveX = true let isAllowMoveY = true // 处理 return { x: isAllowMoveX, y: isAllowMoveY, } } }源码中isAllowMoveNode(BaseNodeModel.ts)会将节点自身moveRules与全局graphModel.nodeMoveRules合并后逐一执行,返回boolean或{x, y}的联合结果。move、moveTo、getMoveDistance都通过它做移动前校验(isIgnoreRule为 true 时可跳过)。
移动相关方法:move、moveTo 与 moveText
moveText(deltaX: number, deltaY: number):移动文本,实现方式是更新text.x/text.y(BaseNodeModel.ts)。moveTo(x: number, y: number, isIgnoreRule: boolean = false):移动到目标绝对坐标,返回值boolean。
this.node.moveTo(200, 100)move(deltaX: number, deltaY: number, isIgnoreRule: boolean = false):相对移动,返回值boolean。
this.move(0, 24 / 2);getMoveDistance(deltaX, deltaY, isIgnoreRule = false):计算实际允许的移动距离,返回值Model.VectorType(即[number, number])。注意该方法会真正执行移动(x/y和文本同步位移),同时返回[moveX, moveY]供graphModel.moveNode驱动边跟随移动。
从源码实现(BaseNodeModel.ts)可以看到一个值得关注的行为:move会分轴校验,X 轴通过则只移动 X(同步移动文本的 X),Y 轴通过则只移动 Y,且移动后会用新的x/y重算旋转矩阵transform;而moveTo是整体校验(任一轴不允许则整体失败)。
状态与属性操作方法
setSelected(isSelected: boolean = true):设置选中状态。
this.node.setSelected(true);setHovered(isHovered: boolean = true):设置 hover 状态(会联动锚点显示)。setHittable(isHittable: boolean):设置节点是否可交互(源码同时保留setHitable兼容拼写错误)。setIsShowAnchor(isShowAnchor: boolean = true):设置是否显示锚点。setElementState(state: ElementState, additionStateData?: Model.AdditionStateDataType):设置 Node | Edge 等 model 的状态,内部同时维护state与additionStateData(BaseNodeModel.ts)。setZIndex(zIndex: number = 1):设置节点 zIndex。
const nodeModel = lf.getNodeModelById("node_1"); nodeModel.setZIndex(999);setProperties(properties):整体设置节点 properties。注意它通过formatData归一化数据、用mapKeys计算变更键集合,并会触发setAttributes与node:properties-change事件。
lf.on("node:click", ({ data }) => { lf.getNodeModelById(data.id).setProperties({ disabled: !data.properties.disabled, scale: 2, }); });setProperty(key: string, value: unknown):设置单个 property,支持'a.b.c'形式的嵌套路径(内部使用 lodash 的set,见 BaseNodeModel.ts)。deleteProperty(key: string):删除节点的某个属性。
lf.on("node:click", ({ data }) => { lf.getNodeModelById(data.id).deleteProperty("disabled"); lf.getNodeModelById(data.id).deleteProperty("scale"); });updateText(value: string):修改节点文本内容,参数value为必传的文本值。
const nodeModel = lf.getNodeModelById("node_1"); nodeModel.updateText("hello world");updateAttributes(attributes: LogicFlow.AttributesType):直接合并更新一组属性(内部为assign(this, attributes))。setStyle(key: string, value: unknown)/setStyles(styles: LogicFlow.CommonTheme)/updateStyles(styles: LogicFlow.CommonTheme):运行时动态修改节点样式。其中setStyle/setStyles是合并进this.style,而updateStyles是整体替换(见 BaseNodeModel.ts)。setEnableRotate(flag: boolean):设置是否启用旋转。
实战小结:自定义一个业务节点的完整模板
综合以上 API,一个典型的自定义业务节点通常组合使用如下能力(以文档中的"结束节点"为例):
- 继承对应形状的 Model(
CircleNodeModel/RectNodeModel等); - 用
initNodeData处理入参图数据; - 用
setAttributes让properties驱动形状(如按scale缩放宽高); - 用
getNodeStyle等样式方法定制 SVG 外观; - 用
getConnectedSourceRules/getConnectedTargetRules约束连线方向; - 用
getDefaultAnchor定制锚点位置与edgeAddable; - 用
moveRules/isAllowMoveNode约束移动; - 业务数据一律存
properties,用getData/getProperties读取,用setProperty/setProperties/deleteProperty修改。
延伸阅读
- 运行时 Model 另一核心:graphModel.zh.md
- 主题与默认样式说明:theme.zh.md
- 自定义节点的形状属性设置:node.zh.md
- 各类形状节点 Model 的源码实现:model/node 目录
- 基础 Model 接口定义(含
ConnectRule、NodeMoveRule、IsAllowMove等类型):BaseModel.ts - 动态分组插件中基于 nodeModel 规则 API 的实战案例:dynamic-group/model.ts
【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架,支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考