[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:graph-line-animation-presets":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 由预设驱动的动画图谱连线\n\n## 这个示例构建了什么\n\n这个示例在深色画布上构建了一个从左到右展开的 relation graph 树，并把连线变成一个动画效果展示集。节点保持小巧并聚焦于图标，而连线则通过十二种可选预设承担了大部分视觉表现。\n\n用户可以在一个浮动控制窗口中切换基础、管线式以及基于 SVG 过滤器的连线样式。还可以拖动或最小化该窗口，打开一个共享设置浮层来修改滚轮和拖拽行为，并将图谱导出为图片。这个 demo 的重点在于，它通过重设 relation-graph 内置连线表面的样式来实现这一切，而不是替换连线渲染器。\n\n## 数据是如何组织的\n\n图谱数据在 `initializeGraph()` 内以内联方式组装为一个静态 `RGJsonData` 对象。它包含一个 `rootId`、一个带有 `id`、`text` 和 `data.icon` 的 `nodes` 数组，以及一个显式包含 `id`、`from`、`to` 和 `text` 字段的 `lines` 数组。\n\n在调用 `setJsonData()` 之前没有做结构预处理。唯一有实际意义的转换是图标查找层：`node.data.icon` 会在 `IconMapper` 中映射为一个 Lucide 组件，而所选的连线预设会在加载后通过修改每一条已有连线来应用。在真实应用中，同样的数据形状可以表示服务依赖、生产阶段、供应路线或审批流，此时拓扑结构保持稳定，但视觉状态需要在运行时切换。\n\n## relation-graph 是如何使用的\n\n`index.tsx` 用 `RGProvider` 包裹整个示例，`MyGraph.tsx` 则以一个从左侧生长的树形布局渲染 `RelationGraph`。这些选项固定了视觉外壳：圆形节点、曲线连线、左右连接点、明确的树形横向和纵向间距、透明节点主体，以及一个位于右下角的横向工具栏。\n\n该示例使用 `RGHooks.useGraphInstance()` 作为运行时控制接口。这个实例负责加载静态 JSON、将图谱居中、缩放以适配视口、通过 `getLines()` 读取当前连线、通过 `updateLine()` 重写它们，并通过 `prepareForImageGeneration()` 和 `restoreAfterImageGeneration()` 支持导出。共享设置浮层使用 `RGHooks.useGraphStore()` 读取当前的滚轮和拖拽模式，然后调用 `setOptions()` 进行切换。\n\n自定义渲染钩子是 `RGSlotOnNode`。这个示例没有替换边，而是保留 relation-graph 原生的连线渲染器，只用基于图标的圆形节点主体覆盖节点内容。样式工作放在 SCSS 中完成：`.rg-map` 和 `.rg-toolbar` 设定深色外壳，`.rg-node-peel.rg-node-checked` 和 `.rg-line-peel.rg-line-checked` 调整选中状态，而十二个 `.my-line-class-xx` 选择器会重新定义 `.rg-line`、`.rg-line-bg`、`.rg-line-label` 和 `.rg-line-text` 的样式。`MySvgFilters` 注入了由过滤器驱动的预设所需的 `rough-paper`、`electric-glitch` 和 `gooey-plasma` 过滤器定义，而像 `current-animation-is-10` 这样的包装类则让节点强调色与当前激活的连线样式保持一致。\n\n## 关键交互\n\n- 在任意 `SimpleUISelect` 中选择一个预设，都会重写每条连线的 `className` 和可见文本，因此整个图谱会一次性切换样式。\n- 初始加载本身也是交互式的：在 `setJsonData()` 之后，图谱会先居中、适配视口，然后立即切换到预设 `10`，因此 demo 打开时就是完整样式状态。\n- 浮动控制窗口可以通过标题栏拖动、最小化，并在不影响图谱本身的情况下重新打开。\n- 设置浮层会在 `scroll`、`zoom` 和 `none` 之间切换 `wheelEventAction`，并在 `selection`、`move` 和 `none` 之间切换 `dragEventAction`。\n- 导出操作会先准备图谱 DOM 以供捕获，然后通过 `modern-screenshot` 将其渲染为 Blob，下载图片，最后再恢复图谱状态。\n\n## 关键代码片段\n\n下面这段代码表明，该 demo 依赖的是 relation-graph 内置的布局和渲染器设置，而不是自定义几何绘制。\n\n```tsx\nconst graphOptions: RGOptions = {\n    defaultLineColor: 'rgba(255, 255, 255, 0.6)',\n    defaultNodeColor: 'transparent',\n    defaultNodeShape: RGNodeShape.circle,\n    defaultLineShape: RGLineShape.StandardCurve,\n    defaultJunctionPoint: RGJunctionPoint.lr,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 310,\n        treeNodeGapV: 70\n    }\n};\n```\n\n下面这段代码展示了：一个固定数据集只加载一次，然后在图谱已经显示之后再应用样式。\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        // node records omitted\n    ],\n    lines: [\n        // line records omitted\n    ]\n};\n\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\nchangeAllLineClassName('10');\n```\n\n下面这段代码是核心技巧：它通过 graph instance API 批量更新所有现有连线。\n\n```tsx\nconst changeAllLineClassName = (newClassName: string) => {\n    setLineAnimation(newClassName);\n    const allLines = graphInstance.getLines();\n    allLines.forEach(line => {\n        graphInstance.updateLine(line.id, {\n            className: `my-line-class-${newClassName}`,\n            text: `className=${newClassName}`\n        });\n    });\n};\n```\n\n下面这段代码展示了，该示例通过 slot 自定义节点，同时将边渲染继续交给 relation-graph 本身。\n\n```tsx\n\u003CRGSlotOnNode>\n    {({ node }: RGNodeSlotProps) => {\n        const iconName = node.data?.icon || 'default';\n        const IconComponent = IconMapper[iconName] || CircleDot;\n        return (\n            \u003Cdiv className=\"my-icon-node rounded-full h-20 w-20 text-white rounded flex place-items-center justify-center hover:bg-white hover:bg-opacity-40\">\n                \u003CIconComponent size={40} strokeWidth={1.5} />\n            \u003C/div>\n        );\n    }}\n\u003C/RGSlotOnNode>\n```\n\n下面这段代码证明，有些预设并不只是纯 CSS 装饰；它们依赖注入的 SVG 过滤器定义。\n\n```tsx\n\u003Cfilter id=\"electric-glitch\">\n    \u003CfeTurbulence type=\"turbulence\" baseFrequency=\"0.05\" numOctaves=\"2\" result=\"turbulence\">\n        \u003Canimate attributeName=\"baseFrequency\" dur=\"0.1s\" values=\"0.01;0.5;0.02\"\n            repeatCount=\"indefinite\"/>\n    \u003C/feTurbulence>\n    \u003CfeDisplacementMap in=\"SourceGraphic\" in2=\"turbulence\" scale=\"10\" xChannelSelector=\"R\"\n        yChannelSelector=\"G\"/>\n\u003C/filter>\n```\n\n下面这段代码展示了其中一个预设如何把这些过滤器定义绑定到 relation-graph 原生的连线层上。\n\n```scss\n.rg-line-peel.my-line-class-10 {\n    .rg-line-bg {\n        stroke: #f43ce5;\n        filter: url(#electric-glitch);\n    }\n\n    .rg-line {\n        stroke: $glitch-color;\n        animation: glitch-slide-10 0.2s steps(2) infinite;\n        filter: url(#electric-glitch);\n    }\n}\n```\n\n## 这个示例的独特之处\n\n从对比数据来看，这个 demo 的定位是一个用于展示内置动画边样式的预设画廊，而不是一个通用的连线样式定制示例。它少见之处在于：通过浮动面板批量更新每条连线的 `className`，把一棵固定的树切换成十二种可切换样式。\n\n与 `custom-line-style` 相比，它在动态效果的多样性上走得更远，把预设分成基础、管线式和 SVG 过滤器三大家族，而不是停留在较小规模、以 CSS 为主的皮肤集合。与 `line-style1` 相比，它不依赖嵌入在 `RGJsonData` 中的静态 `dashType` 和 `animation` 值；它是通过 `getLines()` 和 `updateLine()` 对已加载完成的图谱重新设定主题。与 `line-style2` 相比，它改变的是整个图谱，而不是只强调选中的那条边。与 `customer-line1` 相比，它保留了 relation-graph 原生的连线渲染器，而不是用 slot 渲染的丝带形几何来替换边的绘制方式。\n\n另一个独特细节是它的协同展示层。SCSS 预设类负责处理连线、标签和文本，而像 `current-animation-is-01`、`10`、`11` 和 `12` 这样的包装类则会重新着色节点图标，以匹配当前选择的效果。这使得该示例成为一个紧凑的整图视觉主题化参考，而不是单一、孤立的动画技巧展示。\n\n## 这种模式还适用于哪里\n\n- 网络运维界面可以保留一张依赖图，通过切换连线皮肤来表示空闲、退化、拥塞和故障中的流量状态，而无需重建数据。\n- 物流或制造业看板可以复用同一套拓扑，仅通过改变连线动态效果来表达油流、冷却液循环、能量传输或运输阻塞。\n- 设计系统演示环境可以把同样的技术用作边样式目录，让产品团队在一个稳定的示例图上比较多种品牌化连线处理方式。\n- 工作流或审批查看器可以将连线预设映射到正常吞吐、升级处理、审核积压或双向同步等运行时状态，同时保持节点布局不变。\n",false,500,1782615375939]