视图控制与效果
目的与范围
本文档介绍 relation-graph 中的视图控制与视觉效果系统,它提供用于操控画布视口并创建平滑视觉过渡的 API。该系统实现于 RelationGraphWith5Zoom 与 RelationGraphWith6Effect 类中,它们构成核心类层级的一部分 参见 3.1。
本文档范围:
- 画布变换操作(缩放、平移)
- 聚焦操作(节点居中、缩放以适配)
- 节点与画布的动画控制
- 与缩放级别联动的性能模式集成
相关主题:
画布坐标系统
relation-graph 的画布使用基于变换的坐标系统,包含两个主要组件:
| 属性 | 类型 | 描述 |
|---|---|---|
canvasOffset |
{x: number, y: number} |
以像素为单位的平移偏移量,表示整个图的平移 |
canvasZoom |
number |
以百分比表示的缩放级别(100 = 100%,50 = 50%,等等) |
这些属性存储在 RGOptions 中,并通过数据提供器访问。所有视图控制操作最终都会修改这些值来变换画布。
坐标变换:
displayX = (nodeX * zoomScale) + canvasOffset.x
displayY = (nodeY * zoomScale) + canvasOffset.y
where zoomScale = canvasZoom / 100
缩放系统架构
缩放系统为缩放操作提供两个主要方法:
graph TB
subgraph "公共 API"
zoom["zoom(buff, userZoomCenter?, e?)"]
setZoom["setZoom(finalZoom, userZoomCenter?)"]
end
subgraph "缩放计算"
bounds["检查最小/最大边界
(minCanvasZoom, maxCanvasZoom)"]
event["触发 beforeZoomStart 事件"]
scale["计算新比例
oldScale + (buff/100)"]
offset["调整 canvasOffset
以保持缩放中心"]
end
subgraph "副作用"
perfHook["_performanceModeLogicHook"]
editView["_updateEditingControllerView"]
dataUpdate["_dataUpdated"]
zoomEvent["触发 onZoomEnd 事件"]
end
subgraph "性能模式逻辑"
checkThreshold{"newZoom <= 40?"}
showEasy["设置 showEasyView = true"]
hideEasy["设置 showEasyView = false"]
updateLines["updateElementLines"]
end
zoom --> event
setZoom --> zoom
event --> bounds
bounds --> scale
scale --> offset
offset --> perfHook
perfHook --> checkThreshold
checkThreshold -->|是| showEasy
checkThreshold -->|否| hideEasy
hideEasy --> updateLines
perfHook --> editView
editView --> dataUpdate
dataUpdate --> zoomEvent图示:缩放操作流程
zoom() 方法
zoom() 方法应用相对缩放调整:
-
参数:
buff:以百分点表示的缩放增量(例如,10 表示 +10%,-10 表示 -10%)userZoomCenter:可选的缩放中心点(客户端坐标)e:可选的滚轮事件对象
-
行为:
- 触发
beforeZoomStart事件(如果返回true可中止) - 将缩放限制在
minCanvasZoom与maxCanvasZoom边界内 - 计算新比例:
newScale = (canvasZoom + buff) / 100 - 调整画布偏移以保持缩放中心位置
- 在选项中更新
canvasOffset与canvasZoom - 调用性能模式逻辑钩子
- 触发
onZoomEnd事件并携带新旧缩放值
- 触发
缩放中心计算:
mouseX = userZoomCenter.x - viewRectBox.left
mouseY = userZoomCenter.y - viewRectBox.top
newX = mouseX - (mouseX - canvasOffset.x) * (newScale / oldScale)
newY = mouseY - (mouseY - canvasOffset.y) * (newScale / oldScale)
该公式确保鼠标指针下(或指定中心点下)的点在缩放过程中保持静止。
setZoom() 方法
setZoom() 方法设置绝对缩放级别:
-
参数:
finalZoom:目标缩放级别(百分比,例如 100 表示 100%)userZoomCenter:可选的缩放中心点
-
实现: 计算与当前缩放的差值并调用
zoom():const buff = Math.round(finalZoom - options.canvasZoom); this.zoom(buff, userZoomCenter);
性能模式集成
缩放系统通过 _performanceModeLogicHook() 方法与性能模式集成,该方法会根据缩放级别自动切换渲染模式:
| 缩放级别 | 渲染模式 | 行为 |
|---|---|---|
| > 40% | 标准 SVG | 完整的 SVG 渲染,包含所有细节 |
| ≤ 40% | 简易视图(Canvas) | 为性能而简化的基于画布的渲染 |
逻辑流程:
graph LR
zoomChange["缩放变化"]
oldCheck{"oldZoom <= 40?"}
newCheck1{"newZoom > 40?"}
newCheck2{"newZoom <= 40?"}
hideEasy["showEasyView = false
updateElementLines()"]
showEasy["showEasyView = true"]
noChange["无变化"]
zoomChange --> oldCheck
oldCheck -->|是| newCheck1
oldCheck -->|否| newCheck2
newCheck1 -->|是| hideEasy
newCheck1 -->|否| noChange
newCheck2 -->|是| showEasy
newCheck2 -->|否| noChange图示:性能模式切换逻辑
40% 阈值在视觉质量与渲染性能之间提供了平衡。当缩小以查看大型图时,系统会自动切换到基于画布的渲染 参见 6.3。
平移操作
平移操作通过修改 canvasOffset 属性来移动图的可视区域。主要方法是 setCanvasCenter():
setCanvasCenter()
将特定画布坐标移动到视口中心:
setCanvasCenter(x: number, y: number) {
this.dataProvider.setCanvasCenter(x, y);
this._dataUpdated();
}
数据提供器会计算将给定坐标居中所需的偏移量:
canvasOffset.x = (viewSize.width / 2) - (x * zoomScale)
canvasOffset.y = (viewSize.height / 2) - (y * zoomScale)
聚焦操作
RelationGraphWith6Effect 类提供了若干用于聚焦到特定节点或内容区域的高层操作。
聚焦操作方法
graph TB
subgraph "公共 API"
focusById["focusNodeById(nodeId)"]
zoomToFit["zoomToFit(nodes?)"]
moveCenter["_moveToCenter(nodes?)"]
fitHeight["fitContentHeight(padding)"]
end
subgraph "计算"
getNode["getNodeById()
获取节点对象"]
getRect["getNodesRectBox()
计算包围盒"]
getCenter["getNodesCenter()
计算中心点"]
end
subgraph "变换操作"
setZoomOp["setZoom()"]
setCenterOp["setCanvasCenter()"]
setOffsetOp["setCanvasOffset()"]
end
focusById --> getNode
getNode --> focusNode["focusNode(thisNode)"]
focusNode --> setZoomOp
focusNode --> setOffsetOp
zoomToFit --> getRect
zoomToFit --> moveCenter
getRect --> calcZoom["计算适配的缩放值"]
calcZoom --> setZoomOp
moveCenter --> getCenter
getCenter --> setCenterOp
fitHeight --> getRect
fitHeight --> setCenterOp
fitHeight --> setZoomOp图示:聚焦操作方法及其依赖关系
zoomToFit()
自动计算用于显示指定节点的最佳缩放级别与位置:
算法:
- 使用
getNodesRectBox()计算目标节点的包围盒 - 在加入内边距后,分别计算两个维度的缩放比例:
zoomPercentX = viewWidth / (boxWidth + padding * 2) zoomPercentY = viewHeight / (boxHeight + padding * 2) - 取较小的比例(并限制最大为 100%):
zoomPercent = Math.min(zoomPercentX, zoomPercentY, 1) - 使用
_moveToCenter()将节点居中 - 使用
setZoom()应用计算出的缩放值
默认行为: 如果未指定节点,则对图中的所有节点生效。
focusNodeById()
根据节点 ID 聚焦到特定节点:
- 使用
getNodeById(nodeId)获取节点 - 若找到,则调用私有
focusNode()方法 - 将缩放设置为 100%
- 计算偏移量以将节点置于视口中心
- 更新
checkedNodeId选项以标记被聚焦的节点
居中计算:
finalX = -nodeX + (viewWidth / 2) - (nodeWidth / 2)
finalY = -nodeY + (viewHeight / 2) - (nodeHeight / 2)
fitContentHeight()
在保持宽度的同时,将画布高度调整为恰好适配内容:
过程:
- 临时将画布不透明度设置为 0.01(几乎不可见)
- 计算节点包围盒
- 确定缩放因子:
scale = min(1, viewWidth / nodesWidth) - 计算新高度:
newHeight = nodesHeight * scale + padding * 2 - 更新视图尺寸选项
- 在 200ms 延迟后:
- 应用计算出的缩放
- 使用
_moveToCenter()将内容居中 - 将画布不透明度恢复为 1
该方法适用于希望图在无需滚动的情况下完全可见的场景,例如嵌入式小组件或报告生成。
动画系统
relation-graph 动画系统提供两套相互独立的动画控制:
节点位置动画
用于在节点位置变化时(例如布局过程中)控制平滑过渡:
| 方法 | 效果 |
|---|---|
enableNodeXYAnimation() |
启用节点的平滑位置过渡 |
disableNodeXYAnimation() |
节点直接跳到新位置 |
由 RGOptions 中的 enableNodeXYAnimation 选项控制。启用后,CSS 过渡或框架特定的动画机制会处理平滑移动。
画布变换动画
用于控制画布缩放与平移操作的平滑过渡:
| 方法 | 效果 |
|---|---|
enableCanvasAnimation() |
启用平滑缩放/平移过渡 |
disableCanvasAnimation() |
画布变换立即生效 |
由 enableCanvasTransformAnimation 选项控制。启用后可在导航时提供更平滑的用户体验,但在大型图上可能影响性能。
效果操作集成
下图展示了视图控制操作如何与更广泛的系统集成:
graph TB
subgraph "用户/API 触发"
userZoom["鼠标滚轮
双指捏合手势"]
apiCall["API 调用
setZoom, zoomToFit,
focusNodeById"]
layoutDone["布局完成"]
end
subgraph "RelationGraphWith5Zoom"
zoomMethod["zoom()"]
setZoomMethod["setZoom()"]
end
subgraph "RelationGraphWith6Effect"
zoomToFitMethod["zoomToFit()"]
focusMethod["focusNodeById()"]
fitHeightMethod["fitContentHeight()"]
animationToggles["启用/禁用
NodeXYAnimation
CanvasAnimation"]
end
subgraph "数据提供器"
updateOpts["updateOptions()
canvasZoom
canvasOffset
enableNodeXYAnimation
enableCanvasTransformAnimation"]
setCenterMethod["setCanvasCenter()"]
setOffsetMethod["setCanvasOffset()"]
end
subgraph "响应式更新"
dataUpdated["_dataUpdated()"]
updateHook["updateViewHook()"]
frameworkReact["框架响应式机制
Vue refs, React state"]
end
subgraph "视图渲染"
componentRerender["组件重新渲染"]
canvasRedraw["Canvas/EasyView 更新"]
end
userZoom --> zoomMethod
apiCall --> setZoomMethod
apiCall --> zoomToFitMethod
apiCall --> focusMethod
apiCall --> fitHeightMethod
apiCall --> animationToggles
layoutDone --> zoomToFitMethod
setZoomMethod --> zoomMethod
zoomToFitMethod --> setZoomMethod
focusMethod --> setZoomMethod
fitHeightMethod --> setZoomMethod
zoomMethod --> updateOpts
zoomToFitMethod --> setCenterMethod
focusMethod --> setOffsetMethod
fitHeightMethod --> setCenterMethod
animationToggles --> updateOpts
setCenterMethod --> updateOpts
setOffsetMethod --> updateOpts
updateOpts --> dataUpdated
dataUpdated --> updateHook
updateHook --> frameworkReact
frameworkReact --> componentRerender
componentRerender --> canvasRedraw图示:视图控制集成流程
所有视图控制操作遵循一致的模式:
- 调用 API 方法(用户交互或程序调用)
- 执行变换计算
- 通过数据提供器更新选项
_dataUpdated()触发响应式更新- 框架特定的渲染更新视图
关键实现细节
缩放边界约束
缩放系统强制执行可配置的边界,以防止出现极端缩放级别:
minCanvasZoom:允许的最小缩放(默认值因框架而异)maxCanvasZoom:允许的最大缩放(默认值因框架而异)
当调用 zoom() 时,该方法会自动对最终缩放值进行夹取:
if ((options.canvasZoom + buff) < options.minCanvasZoom) {
buff = options.minCanvasZoom - options.canvasZoom;
} else if ((options.canvasZoom + buff) > options.maxCanvasZoom) {
buff = options.maxCanvasZoom - options.canvasZoom;
}
如果夹取后的 buff 变为 0,则操作被中止。
事件集成
视图控制操作会在关键节点触发事件:
| 事件 | 触发方 | 参数 | 可取消 |
|---|---|---|---|
beforeZoomStart |
zoom() |
(currentZoom, buff, wheelEvent) |
是(返回 true) |
onZoomEnd |
zoom() |
(newZoom, oldZoom) |
否 |
beforeZoomStart 事件可通过返回 true 来阻止缩放操作,从而允许父组件实现自定义的缩放限制。
视图矩形查询
多个操作依赖 getViewBoundingClientRect(),它以客户端坐标获取当前视口尺寸。这对于以下场景至关重要:
- 计算缩放中心点
- 确定画布到客户端坐标的变换
- 基于视口的可见性检查
该方法查询实际 DOM 元素的 bounding client rect,确保即使图嵌入在复杂布局中也能获得准确定位。
总结
视图控制与效果系统提供:
- 通过
zoom()与setZoom()实现 缩放操作,并自动保持中心点不变 - 通过
setCanvasCenter()与直接操控偏移实现 平移操作 - 包括
zoomToFit()、focusNodeById()、fitContentHeight()的 聚焦操作 - 同时支持节点位置与画布变换的 动画控制
- 在缩放阈值处自动切换渲染模式的 性能模式集成
- 支持自定义行为与取消的 事件驱动架构
所有操作都通过数据提供器协同,并触发响应式更新,确保在 Vue 2/3、React 与 Svelte 实现中行为一致 参见 10。