ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LogicFlow 画布实例 API 完全指南:resize、视口变换、坐标转换与边动画

LogicFlow 画布实例 API 完全指南:resize、视口变换、坐标转换与边动画 LogicFlow 画布实例 API 完全指南resize、视口变换、坐标转换与边动画【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlowLogicFlow专注于业务自定义的流程图编辑框架将画布尺寸管理、视口缩放平移、坐标换算与元素层级/边动画等能力统一封装为LogicFlow 实例instance方法。本文基于官方 API 文档 canvas.en.md 为主线结合 LogicFlow.tsx、GraphModel.ts 与 TransformModel.ts 的源码实现系统讲解每一个画布相关实例 API 的签名、参数、返回值与底层原理读完即可在业务项目中熟练完成画布自适应、视口定位、缩放控制、坐标换算、连线流动动画等常见实操。适用前提以下所有 API 均需在new LogicFlow({ container, ... })创建实例成功后调用部分依赖图数据的方法如fitView、translateCenter在画布为空时不会产生效果。一、画布尺寸resize 与容器自适应签名resize(width?: number, height?: number): void参数名称类型必填说明widthnumber否目标宽度缺省时自动从容器计算heightnumber否目标高度缺省时自动从容器计算用法lf.resize() // 从容器重新读取宽高 lf.resize(1200, 800) // 指定宽高从 LogicFlow.tsx 的实现可以看到resize委托给graphModel.resize并将结果同步回实例的optionsresize(width?: number, height?: number): void { this.graphModel.resize(width, height) this.options.width this.graphModel.width this.options.height this.graphModel.height }而 GraphModel.ts 中的底层实现带有完整的防御性检查实例已销毁rootEl不存在、容器不在 DOM 中、或容器不可见offsetParent null时直接返回宽高取值遵循width ?? getBoundingClientRect().width的优先级——传了就用传入值没传就测量容器实际尺寸并记录isContainerWidth / isContainerHeight标记以便监听容器尺寸变化。这正是省略参数时按容器重新计算宽高的源码级依据也是响应式布局下容器尺寸变化后需要手动调用lf.resize()的原因。二、视口定位focusOn、translateCenter、fitViewfocusOn将元素或坐标移到视口中心签名focusOn(focusOnArgs: { id?: string; coordinate?: { x: number; y: number } }): void参数名称类型必填说明focusOnArgsobject是二选一提供id或coordinate用法// 让某个节点/边的中心移动到画布视口中心 lf.focusOn({ id: node_1 }) // 让某个画布坐标成为视口中心 lf.focusOn({ coordinate: { x: 200, y: 300 } })底层调用链为LogicFlow.focusOn→focusByElement(id)或focusByCoordinate(coordinate)→transformModel.focusOn(x, y, width, height)见 LogicFlow.tsx。值得注意两点实现细节节点与边的定位基准不同按id定位时节点取其中心点nodeModel.getData().x/y而边取的是文本位置edgeModel.textPositionLogicFlow.tsx因为边没有单一中心点。TransformModel.focusOn的核心计算是把目标画布点换算成 HTML 坐标再算出让该点位于容器中心所需的平移增量TransformModel.tsfocusOn(targetX, targetY, width, height) { const [x, y] this.CanvasPointToHtmlPoint([targetX, targetY]) const [deltaX, deltaY] [width / 2 - x, height / 2 - y] this.TRANSLATE_X deltaX this.TRANSLATE_Y deltaY }translateCenter图形整体居中签名translateCenter(): void将当前所有节点组成的虚拟矩形平移到画布中心。源码见 GraphModel.ts先通过getVirtualRectSize()求出所有节点含宽高与描边strokeWidth的包围盒及其中心坐标再调用transformModel.focusOn将该中心移动到容器中心。注意空画布无节点时直接return不会产生任何效果。fitView图形自适应视口签名fitView(verticalOffset?: number, horizontalOffset?: number): void参数名称类型必填默认值说明verticalOffsetnumber否20图形距视口上下边缘的留白horizontalOffsetnumber否20图形距视口左右边缘的留白fitView在内部同时完成缩放 平移两步GraphModel.ts先按虚拟矩形与容器尺寸的比例计算缩放比zoomRatio 1 / Math.max(zoomRatioX, zoomRatioY)以容器中心为锚点调用transformModel.zoom再调用focusOn把虚拟矩形居中。同时 LogicFlow.tsx 做了兼容处理只传一个参数时自动复用为水平与垂直两个方向的 offset。实战建议fitView常用于加载数据后一键适应屏幕例如在graph:rendered事件或lf.render(data)之后调用lf.render(graphData) lf.fitView() // 20px 留白自适应 lf.fitView(50) // 上下左右均 50px 留白 lf.fitView(50, 100) // 上下 50px左右 100px三、缩放控制zoom、resetZoom 与缩放边界zoom按刻度或按比例缩放签名zoom(zoomSize?: boolean | number, point?: [number, number]): string参数与返回值名称类型必填说明zoomSizeboolean \| number否number直接设定缩放比例1缩小、1放大true/false按内置刻度ZOOM_SIZE默认0.04步进放大/缩小point[number, number]否缩放锚点画布坐标返回值string——当前缩放比例百分比字符串例如120%。用法lf.zoom() // 默认按刻度缩小一档当前比例 - 0.04 lf.zoom(true) // 按刻度放大一档 lf.zoom(1.2) // 直接缩放到 120% lf.zoom(0.8, [100, 100]) // 以 (100,100) 为锚点缩放到 80%源码实现位于 TransformModel.ts传入数字时直接作为新比例传入布尔值时以ZOOM_SIZE0.04为步长增减若超出[MINI_SCALE_SIZE, MAX_SCALE_SIZE]边界则忽略本次缩放指定point锚点时会同步调整平移量TRANSLATE_X - (newScaleX - SCALE_X) * point[0]保证缩放围绕锚点进行类似地图应用的以鼠标位置为中心缩放。每次缩放都会触发GRAPH_TRANSFORM事件并返回格式化后的百分比字符串。resetZoom重置缩放签名resetZoom(): void将SCALE_X与SCALE_Y恢复为1TransformModel.ts即回到 100% 原始比例。设置缩放边界签名setZoomMiniSize(size: number): void setZoomMaxSize(size: number): void方法说明源码默认值setZoomMiniSize设置允许缩小的最小倍数0.2见 TransformModel.tsMINI_SCALE_SIZE 0.2setZoomMaxSize设置允许放大的最大倍数16见 TransformModel.tsMAX_SCALE_SIZE 16用法lf.setZoomMiniSize(0.5) // 最多缩小到 50% lf.setZoomMaxSize(4) // 最多放大到 400%四、平移控制translate 与 resetTranslatetranslate相对位移签名translate(x: number, y: number): void按相对偏移量平移画布。底层 TransformModel.ts 在累加TRANSLATE_X / TRANSLATE_Y前会校验平移边界translateLimitMinX/MaxX等默认±Infinity即不限制该限制可通过构造参数stopMoveGraph设置取值为false | true | vertical | horizontal | [minX, minY, maxX, maxY]见 TransformModel.ts。resetTranslate还原初始位置签名resetTranslate(): void将平移恢复为初始状态。实现方式是读取当前平移量并反向平移以抵消LogicFlow.tsxresetTranslate(): void { const { TRANSLATE_X, TRANSLATE_Y } transformModel this.translate(-TRANSLATE_X, -TRANSLATE_Y) }五、读取当前变换状态getTransform签名getTransform(): { SCALE_X: number; SCALE_Y: number; TRANSLATE_X: number; TRANSLATE_Y: number; }返回当前画布的缩放与平移组合。直接透出TransformModel的四个 observable 状态LogicFlow.tsx字段含义SCALE_XX 轴缩放比例默认1SCALE_YY 轴缩放比例默认1TRANSLATE_XX 轴平移距离默认0TRANSLATE_YY 轴平移距离默认0这些状态最终由 getTransformStyle 拼装为 CSSmatrix(SCALE_X, SKEW_Y, SKEW_X, SCALE_Y, TRANSLATE_X, TRANSLATE_Y)变换作用于画布渲染层因此getTransform()读到的正是渲染所采用的真实变换值。六、坐标转换getPointByClient签名getPointByClient(x: number, y: number): { domOverlayPosition: { x: number; y: number }; canvasOverlayPosition: { x: number; y: number }; }参数名称类型必填说明x/ynumber是页面client坐标通常来自事件对象e.clientX / e.clientY将相对于页面左上角的事件坐标转换为以画布左上角为原点的两套坐标domOverlayPositionDOM 覆盖层HTML 层坐标包含当前平移与缩放的影响canvasOverlayPositionSVG 覆盖层画布层坐标即真实的画布逻辑坐标已剔除平移与缩放。源码实现GraphModel.ts先通过rootEl.getBoundingClientRect()减去容器左上角得到 DOM 层坐标再调用transformModel.HtmlPointToCanvasPoint逆变换为画布层坐标const bbox this.rootEl.getBoundingClientRect() domOverlayPosition { x: x1 - bbox.left, y: y1 - bbox.top } ;[x, y] this.transformModel.HtmlPointToCanvasPoint([domOverlayPosition.x, domOverlayPosition.y])而HtmlPointToCanvasPoint的公式为(html - translate) / scaleCanvasPointToHtmlPoint的公式为canvas * scale translateTransformModel.ts。这组互逆函数也是 LogicFlow 内部判断点是否落在元素区域内isElementInArea的基础。典型场景在自定义右键菜单、拖拽放置DnD或点击命中检测中需要把鼠标事件坐标换算为画布坐标lf.on(blank:click, ({ e }) { const point lf.getPointByClient(e.clientX, e.clientY) console.log(point.domOverlayPosition) // DOM 层坐标 console.log(point.canvasOverlayPosition) // 画布层坐标 })七、元素层级toFront签名toFront(id: string): void将指定节点或边提升到最顶层。底层 GraphModel.ts 依据堆叠模式overlapMode分三种行为静态模式OverlapMode.STATIC不做任何处理递增模式OverlapMode.INCREASE将元素 zIndex 设为当前最大 zIndex 1setElementZIndex(id, top)默认模式节点在上 / 边在上先把此前置顶元素恢复原层级this.topElement?.setZIndex()再把目标元素设为最大 zIndexELEMENT_MAX_Z_INDEX并记录为新的topElement。用法lf.toFront(node_2) // 将 node_2 置顶若需要更精细的层级控制还可使用底层方法graphModel.setElementZIndex(id, zIndex | top | bottom)GraphModel.ts。八、边动画openEdgeAnimation 与 closeEdgeAnimation签名openEdgeAnimation(edgeId: string): void closeEdgeAnimation(edgeId: string): void分别开启/关闭某条边的流动动画。实现上非常轻量LogicFlow委托给graphModel再调用对应edgeModel.openEdgeAnimation()/closeEdgeAnimation()本质只是翻转边模型上的isAnimation布尔标记BaseEdgeModel.ts、GraphModel.ts。用法lf.openEdgeAnimation(edge_1) // 开启边流动动画 lf.closeEdgeAnimation(edge_1) // 关闭边流动动画动画样式定制动画的视觉表现由主题中的edgeAnimation配置驱动默认值如下constant/theme.tsedgeAnimation: { stroke: #4271DF, strokeDasharray: 12,4,6,4, strokeDashoffset: 100%, animationName: lf_animate_dash, animationDuration: 20s, animationIterationCount: infinite, animationTimingFunction: linear, animationDirection: normal, }BaseEdgeModel.isAnimation为true时边视图会用edgeAnimation样式渲染见 BaseEdgeModel.ts 与 BezierEdge.tsx、LineEdge.tsx 中的动画样式应用。对于需要流动节奏更快的场景可重写边的getEdgeAnimationStyle()方法自定义样式BaseEdgeModel.tsclass AnimatedEdge extends LineEdge { getEdgeAnimationStyle() { const style super.getEdgeAnimationStyle() style.stroke blue style.animationDuration 30s style.animationDirection reverse return style } }九、常见组合用法Common recipes原文档给出的经典组合示例覆盖了自适应 → 居中 → 缩放 → 坐标转换 → 定位的完整链路lf.resize(); lf.translateCenter(); lf.zoom(1.2); const point lf.getPointByClient(300, 200); lf.focusOn({ coordinate: point.canvasOverlayPosition });逐行拆解其含义lf.resize()按容器尺寸重建画布宽高lf.translateCenter()让图内容在视口内居中lf.zoom(1.2)整体放大到 120%lf.getPointByClient(300, 200)把页面坐标 (300, 200) 换算为画布层坐标lf.focusOn({ coordinate: point.canvasOverlayPosition })让换算出的画布坐标成为新的视口中心——注意此处必须使用canvasOverlayPosition而不是domOverlayPosition因为focusOn的坐标是画布逻辑坐标混用会导致定位偏移。十、方法速查表方法作用关键参数 / 默认值源码位置resize(width?, height?)重设画布尺寸缺省时取容器尺寸LogicFlow.tsxfocusOn({id\|coordinate})定位元素/坐标到视口中心节点取中心点边取文本位置LogicFlow.tsxzoom(zoomSize?, point?)缩放数字或布尔刻度默认步进0.04返回百分比字符串TransformModel.tsresetZoom()缩放重置为 1—TransformModel.tssetZoomMiniSize(size)最小缩放倍数默认0.2TransformModel.tssetZoomMaxSize(size)最大缩放倍数默认16TransformModel.tsgetTransform()读取缩放与平移返回SCALE_X/SCALE_Y/TRANSLATE_X/TRANSLATE_YLogicFlow.tsxtranslate(x, y)相对平移受stopMoveGraph平移边界约束TransformModel.tsresetTranslate()平移还原反向平移抵消LogicFlow.tsxtranslateCenter()图形整体居中空画布无效GraphModel.tsfitView(vOffset?, hOffset?)图形自适应视口默认留白20单参兼容GraphModel.tsgetPointByClient(x, y)页面坐标转画布坐标返回 DOM 层与画布层两套坐标GraphModel.tstoFront(id)元素置顶随overlapMode行为不同GraphModel.tsopenEdgeAnimation(edgeId)开启边动画翻转isAnimationBaseEdgeModel.tscloseEdgeAnimation(edgeId)关闭边动画翻转isAnimationBaseEdgeModel.ts结语画布是流程图编辑器的舞台而上述实例 API 就是控制这个舞台的全部遥控器。理解TransformModel中scale/translate的数学关系坐标互逆变换公式canvas (html - translate) / scale是正确使用getPointByClient与focusOn的前提理解fitView先算缩放比、再居中平移的两步实现则能帮你避开自适应后位置偏移的常见坑。将本文的 API 速查表与实际源码LogicFlow.tsx、TransformModel.ts、GraphModel.ts对照阅读即可在业务中举一反三自由组合出定位节点、居中布局、自适应缩放、坐标命中、连线动效等完整交互体验。【免费下载链接】LogicFlowA flow chart editing framework focus on business customization. 专注于业务自定义的流程图编辑框架支持实现脑图、ER图、UML、工作流等各种图编辑场景。项目地址: https://gitcode.com/GitHub_Trending/lo/LogicFlow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进