LogicFlow edgeModel 完全指南边数据模型、样式自定义与锚点计算的源码级剖析【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlowLogicFlow 中每条边连线都对应一个edgeModel实例所有对边的操作本质上都是对这个 model 的操作。本文以官方 API 文档 edgeModel.zh.md 为骨架结合 BaseEdgeModel.ts、PolylineEdgeModel.ts、BezierEdgeModel.ts、LineEdgeModel.ts 等核心实现系统讲解边 model 的属性体系、样式自定义方法族、数据读写 API 与锚点计算原理。读完本文你将掌握自定义边类型折线、贝塞尔、直线、定制边样式与动画、读写边数据以及调整文本位置的完整能力。数据驱动视图为什么所有操作都围绕 edgeModelLogicFlow 采用「数据驱动视图」的架构渲染层SVG DOM完全由 model 数据推导因此对边的任何操作——选中、拖拽、改样式、改文本——最终都落在edgeModel上。官方文档明确建议大多数情况下不建议直接对edgeModel的属性进行赋值操作而是调用 model 或者 graphModel 上提供的方法。从源码看BaseEdgeModel的大量属性都使用 mobx 的observable装饰如 BaseEdgeModel.ts 中的type、sourceNodeId、targetNodeId、startPoint、endPoint、text、properties、points、pointsList等修改即触发视图重渲染而修改properties的setProperty/setProperties/deleteProperty都统一带action并在尾部调用this.setAttributes()同步派生数据。这就是「调用方法而不是直接改属性」的底层原因。数据属性保存时写入 JSON 的字段数据属性是流程图保存lf.getGraphData()/edgeModel.getData()时真正落盘的数据。名称类型是否必须描述idstring✅边 idtypestring✅边类型sourceNodeIdstring✅开始节点 IdtargetNodeIdstring✅结束节点 IdstartPointPoint✅边的开始坐标endPointPoint✅边的结束坐标textObject/string边文本pointsstring控制边的轨迹pointsListArray控制边的轨迹polyline和bezier有line没有propertiesObject边的自定义属性对照 BaseEdgeModel.ts 的getData()实现可以看到保存时的固定结构id、type、properties、sourceNodeId、targetNodeId、sourceAnchorId、targetAnchorId、startPoint、endPoint仅当text.value非空时才附带text同时在INCREASE/STATIC堆叠模式下会额外写入zIndex。因此如果你希望保存额外业务字段请挂到properties上而不是新增顶层字段。PolylineEdgeModel与BezierEdgeModel重写getData()时额外写入了pointsList分别见 PolylineEdgeModel.ts 与 BezierEdgeModel.ts这正是它们回放轨迹的依据。状态属性自定义边时做细粒度样式判断状态属性一般用于自定义边时基于当前交互状态进行更细粒度的样式显示。名称类型是否必须描述isSelectedboolean✅边是否被选中isHoveredboolean✅边是否在 hover 状态isHitableboolean✅边是否可点击draggableboolean✅边是否可拖动isDraggingboolean✅边是否正在拖动isAnimationboolean✅边是否有动画isShowAdjustPointboolean✅边是否显示边两端的调整点visibleboolean✅边是否显示,1.1.0新增源码中这些状态均有对应方法维护setSelected、setHovered、setHitable/setHittable、openEdgeAnimation/closeEdgeAnimation、setElementState等见 BaseEdgeModel.ts。其中isShowAdjustPoint在initEdgeData阶段直接读取graphModel.editConfigModel.adjustEdgeStartAndEnd初始化BaseEdgeModel.ts即是否显示调整点由画布的adjustEdgeStartAndEnd配置统一控制。形状属性offset 的含义与默认值名称类型是否必须描述offsetnumberpolyline 表示折线转折点基于节点的距离bezier 表示控制曲线调整手柄的长度。各类型边的offset默认值并不相同源码里给出了明确答案折线PolylineEdgeModel当未传入offset时按「箭头与折线重叠长度 5」计算默认值即getDefaultOffset()返回arrowStyle.offset 5PolylineEdgeModel.ts 与 PolylineEdgeModel.ts也可以通过properties.offset传入。贝塞尔BezierEdgeModel默认offset 100从properties.offset读取BezierEdgeModel.ts它决定了起止两个控制手柄的长度进而影响曲线弧度。此外PolylineEdgeModel.setAttributes()会在properties.offset变化时同步offset并重新计算整条路径PolylineEdgeModel.ts这解释了为什么修改折线拐点距离只需更新properties。其它属性引用与运行时信息LogicFlow 在 model 上还维护一批引用/运行时属性开发者可借此获取画布引用、类型标识等信息。名称类型是否必须描述graphModelGraphModel✅整个画布对应的 model详情见zIndexnumber✅节点在 z 轴的高度元素重合时zIndex 高的在上面。默认为 0statenumber✅元素状态不同的状态对应着元素显示效果。DEFAULT 1 默认显示TEXT_EDIT 2 此元素正在进行文本编辑ALLOW_CONNECT 4, 此元素允许作为当前边的目标节点NOT_ALLOW_CONNECT 5, 此元素不允许作为当前边的目标节点BaseTypestring✅当前 model 的基础类型对于边则固定为edge。主要用在节点和边混合的时候识别此model是节点还是边。modelTypestring✅当前 model 的类型可取值有edge,polyline,bezier,linesourceAnchorIdstring-连线起点锚点 idadditionStateDataModel.AdditionStateDataType-传递的额外值targetAnchorIdstring-连线终点锚点 idcustomTextPositionboolean-自定义连线文本位置styleLogicFlow.CommonTheme-边的样式arrowConfig{markerStart, markerEnd}-箭头属性virtualboolean-是否为虚拟连线默认 false。当为 true 时导出数据不会包含此元素。v1.1.24值得注意的实现细节BaseType在源码中是readonly BaseType ElementType.EDGEBaseEdgeModel.ts用于节点与边混合场景的类型识别modelType在三个子类中分别被赋值为polyline、bezier、line对应ModelType.POLYLINE_EDGE、ModelType.BEZIER_EDGE、ModelType.LINE_EDGE基类默认是edgearrowConfig的markerStart/markerEnd默认指向url(#marker-start-${id})/url(#marker-end-${id})并且changeEdgeId()会同步更新这两个引用BaseEdgeModel.tszIndex会根据graphModel.overlapMode自动调整DEFAULT模式恒为 0EDGE_TOP模式为 1INCREASE模式取传入值BaseEdgeModel.ts。样式属性与「getXxxStyle」方法族LogicFlow 中所有边最终都以 SVG DOM 方式渲染SVG 样式属性并不会直接存在edgeModel上。当开发者想为 SVG DOM 添加更多 SVG 属性时需要重写edgeModel上的样式获取方法来动态返回。以下方法全部支持重写返回值即渲染时应用到 SVG 元素上的样式对象。getEdgeStyle自定义边主体样式默认为主题Theme 类型说明中的baseEdge。示例class SequenceFlowModel extends PolylineModel { getEdgeStyle() { const style super.getEdgeStyle(); style.stroke blue; style.strokeDasharray 3 3; return style; } }基类默认实现返回{...graphModel.theme.baseEdge, ...this.style}BaseEdgeModel.ts即「主题默认值 单边动态 style」的合并结果。默认baseEdge主题为stroke: #474747、strokeWidth: 2theme.ts。三个子类还各自叠加了polyline/bezier/line主题段并支持properties.style覆盖如 PolylineEdgeModel.ts。getAdjustPointStyle自定义调整点样式在isShowAdjustPoint为 true 时边两端会显示调整点此方法自定义其样式。返回值LogicFlow.CircleTheme基类实现直接返回主题中的edgeAdjustBaseEdgeModel.ts。getAnimation / getEdgeAnimationStyle自定义流向动画文档描述为「自定义边的流向动画」class CustomBezierModel extends BezierEdgeModel { getAnimation() { const animation super.getAnimation(); animation.stroke blue; return animation; } }说明当前源码中对应方法名为getEdgeAnimationStyle()其注释示例与文档完全一致BaseEdgeModel.ts即「自定义边动画样式」返回LogicFlow.EdgeAnimation。历史文档中的getAnimation是该方法的上游描述重写时请以getEdgeAnimationStyle为准。更完整的动画样式写法getEdgeAnimationStyle() { const style super.getEdgeAnimationStyle(); style.stroke blue; style.animationDuration 30s; style.animationDirection reverse; return style; }动画需配合openEdgeAnimation()开启isAnimation true箭头颜色也会随之切换为动画色见getArrowStyle中对isAnimation的判断BaseEdgeModel.ts。getTextStyle自定义边文本样式默认为主题Theme 类型说明中的edgeTextclass SequenceFlowModel extends PolylineModel { getTextStyle() { const style super.getTextStyle(); style.color blue; style.fontSize 20; return style; } }基类实现为cloneDeep(graphModel.theme.edgeText)BaseEdgeModel.ts。默认edgeText主题包含textWidth: 100等配置theme.ts 起支持调整文本宽度、颜色、字号等。getArrowStyle自定义边箭头样式getArrowStyle() { const style super.getArrowStyle(); style.stroke green; return style; }基类实现会综合getEdgeStyle()、getEdgeAnimationStyle()与主题arrow当边处于动画状态时箭头使用动画色作为 fill/strokeBaseEdgeModel.ts保证箭头与边流向视觉一致。getOutlineStyle自定义选中轮廓样式边被选中时展示其范围的矩形框样式getOutlineStyle() { const style super.getOutlineStyle(); style.stroke none; style.hover.stroke none; return style; }基类实现基于主题edgeOutline且 hover 状态下会合并edgeOutline.hover样式BaseEdgeModel.ts因此可在返回值中同时定制普通与 hover 两种状态。初始化与属性同步initEdgeData、setAttributes、createIdinitEdgeData与setAttributes都可以对edgeModel的属性赋值但两者有本质区别initEdgeData只在初始化的时候调用用于初始化的属性setAttributes除了初始化调用外还会在 properties 发生变化时调用。源码中BaseEdgeModel构造函数依次执行initEdgeData(data)与setAttributes()BaseEdgeModel.ts且setProperty、setProperties、deleteProperty每次都会再次触发setAttributes()。initEdgeData内部还会完成 id 生成、箭头 marker 绑定、adjustEdgeStartAndEnd读取、锚点计算setAnchors、轨迹初始化initPoints与文本格式化formatText的完整链路BaseEdgeModel.ts。重写示例Polyline 边class UserEdgeModel extends PolylineEdgeModel { initEdgeData(data) { super.initEdgeData(data); this.offset 20; } }class UserEdgeModel extends PolylineEdgeModel { setAttributes(data) { super.setAttributes(data) this.offset 20 } }createId支持重写自定义边 id 的生成规则。注意请保证此方法返回 id 的唯一性此方法为同步方法如需异步修改边 id可参考仓库 issuesLogicFlow#272中给出的方案。import { v4 as uuidv4 } from uuid; class UserTaskModel extends RectNodeModel { createId() { return uuidv4(); } }文档示例沿用了节点 model 的写法实际用于边时请继承对应的边 model如PolylineEdgeModel。基类createId()默认返回nullBaseEdgeModel.tsid 生成遵循「自定义 createId graphModel.idGenerator 全局生成器 内置 uuid」的优先级链BaseEdgeModel.ts。数据读写 APIgetData 与 properties 系列getData获取被保存时返回的数据。LogicFlow 有固定边数据格式如需在保存数据上添加内容请添加到properties上。不支持重写此方法子类可重写以附加pointsList但保存格式以官方结构为准const edgeModel lf.getEdgeModelById(edge_1); const edgeData edgeModel.getData();getHistoryData同getData。用于历史记录undo/redo时获取边数据如果希望某个属性变化不引起 history 变化可以重写此方法BaseEdgeModel.ts。getProperties获取边属性不支持重写const edgeModel lf.getEdgeModelById(edge_1); const properties edgeModel.getProperties();实现上通过toJS(this.properties)返回普通对象BaseEdgeModel.ts避免把 mobx 可观察对象泄漏给调用方。setProperties设置边 properties会与现有properties合并并触发重渲染const edgeModel lf.getEdgeModelById(edge_1); edgeModel.setProperties({ // 自定义properties });注意setProperties/setProperty都会在赋值后自动调用setAttributes()因此你在setAttributes中根据properties计算的派生属性如offset会同步更新BaseEdgeModel.ts。deleteProperty删除边的某个属性lf.on(edge:click, ({ data }) { lf.getEdgeModelById(data.id).deleteProperty(disabled); lf.getEdgeModelById(data.id).deleteProperty(scale); });updateText修改边文本内容名称类型必传默认值描述valuestringtrue无文本值const edgeModel lf.getEdgeModelById(edge_1); edgeModel.updateText(hello world);实现上仅替换text.value保留原有位置与可拖拽/可编辑配置BaseEdgeModel.ts。文本位置与锚点计算getTextPosition支持重写自定义连线上文本位置。各类型边的默认实现不同这正是它们文本落点的差异来源直线LineEdgeModel取起终点中点LineEdgeModel.ts折线PolylineEdgeModel取最长一段折线的中点若文本为空且存在双击位置则优先落在双击点PolylineEdgeModel.ts贝塞尔BezierEdgeModel取pointsList中四个点起点、两个控制点、终点的平均坐标pointsList为空时退化为起终点中点BezierEdgeModel.ts。配合customTextPosition true可以完全接管文本坐标GraphModel.handleEdgeTextMove中对该标记单独处理GraphModel.ts。getAnchorPosition获取锚点位置。入参sourceNode: BaseNodeModel, targetPosition: LogicFlow.Position返回值LogicFlow.PointgetBeginAnchor / getEndAnchor内部方法用于计算两节点相连时边的起止端点坐标也是setAnchors的底层依赖// 获取开始锚点 // 入参sourceNode: BaseNodeModel, targetNode: BaseNodeModel // 返回值LogicFlow.Point // 获取结束锚点 // 入参targetNode: BaseNodeModel // 返回值LogicFlow.Point源码中的选择逻辑值得细读getBeginAnchor若传入了sourceAnchorId会优先查找该指定锚点找不到则回退默认并console.warn否则遍历sourceNode的全部锚点选取与targetNode距离最近者BaseEdgeModel.ts。getEndAnchor同理以当前startPoint为参照选择最近锚点BaseEdgeModel.ts。计算距离使用的是工具函数twoPointDistanceedge.ts。一个重要的边界情况当用户自定义getDefaultAnchor()返回空数组时表示不显示锚点、不允许连线锚点可能为undefinedsetAnchors会抛出「无法获取beginAnchor/endAnchor」的错误BaseEdgeModel.ts这是实现无锚点节点时需要注意的坑。通过 graphModel 操作边推荐的入口官方文档建议优先调用graphModel上的方法操作边。核心 API 包括GraphModel.tsgetEdgeModelById(edgeId)按 id 获取边 modelGraphModel.tsaddEdge(edgeConfig)添加边边类型优先级为「自定义 全局 默认」注册的 model 不存在时抛出「找不到对应边」错误GraphModel.tsdeleteEdgeById(id)、deleteEdgeBySourceAndTarget(sourceNodeId, targetNodeId)删除边并触发edge:delete事件GraphModel.tsgetNodeEdges(nodeId)获取某节点上所有相连边GraphModel.tsgetSelectElements()获取选中元素复制时自动排除源/目标节点未选中的悬空边GraphModel.ts。实战小结改样式重写getEdgeStyle/getTextStyle/getArrowStyle/getOutlineStyle/getAdjustPointStyle/getEdgeAnimationStyle返回值基于主题默认值叠加别忘了super调用改行为重写initEdgeData初始化、setAttributesproperties 变化时同步派生值、createId自定义 id、getTextPosition文本位置读数据getData是保存格式的唯一权威出口扩展字段一律放properties写数据优先使用setProperties/deleteProperty/updateText避免直接给observable属性赋值破坏派生链获取 model通过lf.getEdgeModelById(id)拿到边 model 后所有上述方法即可直接调用。更完整的 model 体系说明可继续阅读 graphModel.zh.md 与 主题 Theme 类型说明。【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
