JavaScript is required

HTML元素连线

这个示例在 `RGSlotOnCanvas` 中渲染兴趣小组卡片列表和地图标记,并在选中配对之间重绘一条动画假连线。它是“用 relation-graph 连接普通 HTML 元素”而非“连接图节点”的聚焦参考。

使用 Fake Line 连接 HTML 卡片与地图标记

这个示例构建了什么

这个示例在 relation-graph 画布内部构建了一个双面板界面。左侧显示兴趣小组列表,右侧在背景图片上显示固定的地图标记。选择任意一侧时,都会在匹配的卡片与标记之间重绘一条带动画的连接线。

重点并不在节点渲染。可见的端点都是放置在 RGSlotOnCanvas 中的普通 HTML 元素,然后被注册为可连接目标,这样 relation-graph 就可以在它们之间绘制连线。一个悬浮辅助窗口提供了画布设置和图片导出功能,但这个示例的核心在于 DOM 到 DOM 的连接模式。

数据是如何组织的

这个示例使用了一个小型的内存 InterestGroup[] 数组。每条记录包含:

  • groupId
  • groupName
  • location: { x, y }

loadDataFromRemote() 会填充六条记录,然后在渲染期间,同一个数组会被投影两次:

  • 一次投影为左侧的 group-* 目标
  • 一次投影为右侧的 location-* 目标

这种投影方式让一对一连接模式保持简单。在真实应用中,这份数据可以替换为门店位置、楼层平面图上的部门、拓扑图上的设备,或者两个面板之间的匹配记录。

relation-graph 是如何使用的

  • 图使用固定布局,因为真正有意义的位置来自画布插槽中绝对定位的 HTML,而不是自动节点布局。
  • RelationGraph 提供画布宿主,RGProvider 启用了基于 hook 的工具能力,例如悬浮设置面板。
  • RGSlotOnCanvas 承载了整个列表加地图场景,因此 relation-graph 在这里扮演的是自定义 UI 组合之下的连接层。
  • 每张卡片和每个标记都包裹在 RGConnectTarget 中,这样每个 HTML 元素都会获得一个稳定的 targetId
  • RGHooks.useGraphInstance() 用于启动时的视口处理、fake line 替换、运行时选项变更以及导出前准备。
  • RGHooks.useGraphStore() 用于共享设置面板,以便 UI 能反映当前的拖拽和滚轮模式。
  • 关键的实例 API 包括 moveToCenter()zoomToFit()setEditingLine(null)clearFakeLines()addFakeLines(...)
  • 共享辅助窗口还会使用 prepareForImageGeneration()getOptions()restoreAfterImageGeneration() 来导出当前图视图。
  • 样式大多在 relation-graph 之外通过 SCSS 类处理,包括紫色列表卡片、选中光环、脉冲标记以及带动画的曲线连接线。

关键交互

  • 挂载后,示例会加载六个小组,将视口居中、适配画布,并自动选中小组 a
  • 点击某个小组卡片会高亮该卡片,并将连接线重绘到与之匹配的地图标记。
  • 点击地图标记会从对侧面板触发同样的选择流程。
  • 悬浮说明窗口可以被拖动、最小化和展开。
  • 设置浮层可以将滚轮行为切换为 scrollzoomnone
  • 同一个浮层也可以将拖拽行为切换为 selectionmovenone
  • Download Image 操作会把当前图画布导出为图片。

关键代码片段

这个片段展示了图被配置为一个固定布局宿主,用来承载画布插槽中的内容。

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.border,
    wheelEventAction: 'zoom',
    dragEventAction: 'move',
    layout: {
        layoutName: 'fixed'
    }
};

这个片段是核心选择逻辑:一个当前激活的小组 id 会被转换为一条 fake line,连接两个 HTML 目标 id。

const myFakeLines: JsonLine[] = [{
    id: `fl-${groupId}`,
    from: 'group-' + groupId,
    to: 'location-' + groupId,
    color: 'rgba(159,23,227,0.65)',
    lineWidth: 3,
    lineShape: RGLineShape.StandardCurve,
    fromJunctionPoint: RGJunctionPoint.lr,
    toJunctionPoint: RGJunctionPoint.border,
    animation: 2
}];

这个片段展示了如何先清除上一条连接线,再替换为新的连接线,而不是维护一个持久存在的多边图。

graphInstance.setEditingLine(null);
graphInstance.clearFakeLines();
graphInstance.addFakeLines(myFakeLines);

这个片段展示了左侧 HTML 端点如何在画布插槽内部被注册为连接目标。

<RGConnectTarget
    key={group.groupId}
    targetId={`group-${group.groupId}`}
    junctionPoint={RGJunctionPoint.lr}
    disableDrag={true}
    disableDrop={true}
>
    <div
        className={`w-full pointer-events-auto c-i-group cursor-point ${activeGroupId === group.groupId ? 'c-i-group-checked' : ''}`}
        onClick={() => onGroupClick(group.groupId)}
    >

这个片段展示了滚轮和拖拽行为是在运行时通过图实例动态修改的,而不是通过重建整个示例来实现。

<SettingRow
    label="Wheel Event:"
    options={[
        { label: 'Scroll', value: 'scroll' },
        { label: 'Zoom', value: 'zoom' },
        { label: 'None', value: 'none' },
    ]}
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

这个示例的独特之处

与附近的其他示例相比,这个示例是一个专门用于连接普通 HTML 元素与另一个普通 HTML 元素的参考实现。它不会把目标端转换为图节点,也不会维持一个更大、可编辑的关系模型。

  • element-connect-to-node 相比,两端都通过 RGConnectTarget 保持为 HTML,而不是混合使用 DOM 端点与基于节点的锚点。
  • element-line-edit 相比,地图标记不是图节点,因此这个示例更像是一种查看器式的匹配模式,而不是节点锚点调整演示。
  • interest-group 相比,这里的范围被有意收窄了:一个选中的小组 id、一个选中的标记,以及一条激活的连接线。
  • scene-network-use-canvas-slot 相比,重点不在静态仪表板式的画板,而是在两个 UI 区域之间由点击驱动的焦点转移。

这种少见的组合才是关键:固定布局的画布插槽、成对的 DOM 端点、一条带动画的曲线 fake line、同步的选择状态,以及一个共享的悬浮工具面板。这让这个示例成为一个非常实用的起点,适用于 relation-graph 需要连接界面元素,而不是可视化传统节点连接数据集的场景。

这种模式还适用于哪里

  • 将门店、教室、展位或房间列表链接到楼层平面图或校园地图上的标记。
  • 在不把背景标记转换为图节点的情况下,同时在摘要面板和空间背景中展示一个被选中的资源。
  • 构建主从式仪表板,让某一条被选中的记录高亮自定义画布场景中的匹配元素。
  • 在保持可见场景完全自定义的前提下,跨不同 UI 区域连接普通 DOM 卡片、徽标或标签。