LikeC4 图表初识视口:用 initialZoom 让嵌入式架构图以调用方定义的缩放级别居中显示
2026/9/17 12:18:19 网站建设 项目流程

LikeC4 图表初识视口:用 initialZoom 让嵌入式架构图以调用方定义的缩放级别居中显示

【免费下载链接】likec4Visualize, collaborate, and evolve the software architecture with always actual and live diagrams from your code项目地址: https://gitcode.com/GitHub_Trending/li/likec4

导读:本文围绕@likec4/diagram中新增的initialZoom属性展开,讲解如何让嵌入到 React 应用中的 LikeC4 架构图在首次渲染时,以调用方指定的缩放级别居中呈现,而不是机械地执行"适应全图(fit-to-view)"。你将掌握该属性的用法、它与fitView的优先级关系、底层状态机如何消费该值,以及对应的单元测试如何验证行为,从而在实现嵌入式图表、文档插图与自定义看板时获得更可控的首屏表现。

背景:嵌入式图表的首屏视口问题

@likec4/diagram是 LikeC4 的 React 组件库,用于渲染软件架构图(对应仓库packages/diagram,安装方式见 packages/diagram/README.md)。它基于@xyflow/react构建视口(viewport)系统,并通过 XState 状态机管理图表的初始化、导航与交互。

initialZoom出现之前,嵌入式图表的初始视口只有两种行为:

  • 默认开启fitView,即自动缩放并平移视口,让全部节点与连线恰好完整可见;
  • 关闭fitView时,以缩放级别1(100%)为中心显示(从源码看即 machine.state.initializing.ts 中的context.initialZoom ?? (context.fitView ? undefined : 1))。

这对于"整图总览"场景足够,但实际嵌入场景往往需要更细的控制:例如在文档中嵌入某张图时,希望首屏聚焦于图的中心区域且放大到 75%;或在不同页面上下文里,希望每张图以调用方自定义的缩放级别呈现,而不是统一"适应全图"。

本次变更(对应变更集文件 .changeset/initial-diagram-zoom.md)为@likec4/diagram引入了initialZoom属性,使嵌入式图表能以调用方定义的缩放级别居中启动,并以patch级别的语义发布。

initialZoom 属性:定义与优先级

属性签名

在 LikeC4Diagram.props.ts 中,属性定义如下:

/** * Initial centered zoom level. When set, it overrides automatic fit-to-view. */ initialZoom?: number | undefined

要点:

  • 类型为number | undefined,语义是"初始居中的缩放级别";
  • 一旦设置,会覆盖(override)自动的 fit-to-view 行为;
  • 未设置时,回退到原有逻辑。

与 fitView 的优先级关系

在 LikeC4Diagram.tsx 中:

const initialFitView = initialZoom === undefined && fitView

也就是说,只有"未提供initialZoomfitView为真"时,才会让 XYFlow 层面执行初始 fit;一旦提供了initialZoom,初始的自动适应全图就被显式关闭。

同时,fitView属性的默认值为true(见 LikeC4Diagram.tsx 与 LikeC4Diagram.props.ts),因此:

  • fitView默认开启,未传initialZoom时,图表启动即自动适应全图;
  • 传入initialZoom(例如0.75)后,即使fitView保持默认true,初始视口也改为"以 0.75 倍缩放居中显示",fitView仅在用户交互、重新布局等后续场景继续生效。

受控视口的推导

在 DiagramXYFlow.tsx 中,resolveControlledViewport同样把initialZoom视为"非自动适配"信号:

export function resolveControlledViewport( enableFitView: boolean, initialZoom: number | undefined, bounds: { x: number; y: number }, ): Viewport | undefined { if (enableFitView || initialZoom !== undefined) { return undefined } return { x: -bounds.x, y: -bounds.y, zoom: 1, } }

可见enableFitView || initialZoom !== undefined时不会注入"平移至左上角、zoom=1"的兜底视口;而只有当两者都未启用时,才会回退到{ x: -bounds.x, y: -bounds.y, zoom: 1 }。这与状态机中的zoom: 1兜底行为互相印证。

典型用法示例

initialZoom可直接传给LikeC4Diagram组件,也可随LikeC4ModelProvider包裹的图表一起使用(组件结构参考 LikeC4Diagram.tsx):

import { LikeC4Diagram } from '@likec4/diagram' import { useLikeC4Model } from '@likec4/diagram' export function EmbeddedSystemDiagram() { const model = useLikeC4Model() return ( <LikeC4Diagram view={model.view('systemContext')} // 以 0.75 倍缩放居中展示,覆盖默认的 fit-to-view initialZoom={0.75} /> ) }

可选组合:

  • initialZoom={1}:以 100% 缩放居中首屏,适合直接阅读关键区域;
  • initialZoom={0.5}:整体缩小一半,适合在有限区域内展示较大图的主体部分;
  • 配合controls={false}zoomable={false}等属性(见 LikeC4Diagram.props.ts),可构造纯展示型嵌入式图表;
  • 若希望"自动适应全图"作为初始行为,则省略initialZoom,保持fitView默认开启即可。

需要说明的是:initialZoom只决定初始视口的缩放级别,初始位置始终以视图(view)边界中心为目标(见下节状态机实现),因此"居中 + 指定缩放"是该属性所保证的完整行为。

底层实现:XState 状态机如何消费 initialZoom

参数传递链路

initialZoom从组件属性出发,经 DiagramActorProvider.tsx 传入 XState actor 的输入(Input),在 machine.setup.ts 中作为Input.initialZoom?: number | undefined存储到状态机上下文Context,最终在初始化完成动作中被读取。

初始化状态机中的视口决策

在 machine.state.initializing.ts 中,isReady状态在满足isReady守卫后执行首屏定位:

enqueue(fitDiagram({ duration: 0, zoom: context.initialZoom ?? (context.fitView ? undefined : 1), }))

解读:

  • initialZoom已设置时,fitDiagramzoom: initialZoom执行,且duration: 0表示无动画、直接定位;
  • 未设置且fitView为真时,zoomundefined,由fitDiagram依据视图边界自动计算缩放(参见 machine.actions.layout.ts 中fitBoundsInViewportgetViewportForBounds的调用);
  • 未设置且fitView为假时,使用兜底zoom: 1居中。

从 XState 状态机实现看,fitDiagram最终通过panZoom.setViewport写入 XYFlow 的视口,位置由视图边界(viewBounds)推导,因此初始位置天然落在视图中心。

初始化流程中的双事件同步

状态机注释(machine.state.initializing.ts)说明:图表进入initializing状态后,必须等待两个事件——xyflow.init(XYFlow 实例就绪)与update.view(节点与边数据就绪)——两者都到达后才会过渡到isReady,进而执行上述首屏定位。这保证了initialZoom在数据与渲染实例均可用时才生效,不会出现"数据未到就定位"的竞态。

测试验证:单测如何锁定行为

仓库为本次行为补充了针对性单元测试 machine.state.initializing.spec.ts。该测试构造了一个视口尺寸1000x800、视图边界为{ x: 100, y: 200, width: 1800, height: 1000 }的测试 actor,并验证三种初始视口策略:

  1. 显式 initialZoom 且关闭 fitViewfitView: false, initialZoom: 0.75):断言viewport.zoom等于0.75,且视口中心与视图边界中心一致——即"以调用方定义的缩放居中"(对应测试用例 "uses and centers the explicit initial zoom when fit is disabled");
  2. 开启 fitView 且未指定 zoom:断言缩放接近viewportSize.width / view.bounds.width,即由 fit 计算得出;
  3. 关闭 fitView 且未指定 zoom:断言viewport.zoom等于1且同样居中。

测试中的辅助函数viewportCenter将视口反推为画布坐标中心,与viewBoundsCenter进行相等断言,精确验证了"居中"语义。这套用例同时锁定了三者的优先级关系:initialZoom优先 →fitView推导 →zoom: 1兜底。

实际影响与应用场景

结合@likec4/diagram的定位(React 组件库,可直接使用或由 LikeC4 CLI 生成组件,见 packages/diagram/README.md),initialZoom的引入带来以下实用价值:

  • 文档与站点嵌入:在技术文档或门户页面中嵌入架构图时,可按阅读场景定制首屏缩放,避免"整图过小、细节不可读"的体验问题;
  • 多图统一视觉:多张图并列展示时,可用统一的initialZoom值保持缩放一致性,而非依赖各图边界推导出的不同 fit 比例;
  • 聚焦式展示:与"居中 + 放大"组合,让首屏直接呈现架构图中最重要的区域;
  • 回归成本低:属性可选(number | undefined),不传时行为完全向后兼容,属于patch级别的增量能力。

小结

initialZoom@likec4/diagram对嵌入式图表首屏体验的一次精准增强:它在不破坏fitView默认行为的前提下,赋予调用方"指定缩放 + 居中"的初始化控制权。从组件属性到 XState 状态机的完整链路(LikeC4Diagram.tsx → DiagramActorProvider.tsx → machine.state.initializing.ts),再到针对性的单元测试(machine.state.initializing.spec.ts),均有清晰实现与验证依据,可直接参考 packages/diagram 目录继续深入研读。

【免费下载链接】likec4Visualize, collaborate, and evolve the software architecture with always actual and live diagrams from your code项目地址: https://gitcode.com/GitHub_Trending/li/likec4

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询