View Control & Effects
Purpose and Scope
This document covers the view control and visual effects system in relation-graph, which provides APIs for manipulating the canvas viewport and creating smooth visual transitions. The system is implemented in the RelationGraphWith5Zoom and RelationGraphWith6Effect classes, which form part of the core class hierarchy see 3.1.
Scope of this document:
- Canvas transformation operations (zoom, pan)
- Focus operations (center nodes, zoom-to-fit)
- Animation controls for nodes and canvas
- Performance mode integration with zoom levels
Related topics:
- For user interaction events that trigger view changes, see User Actions & Interactions
- For the mini-map navigation component, see Mini View & Navigation
- For canvas-based rendering in performance mode, see Performance Mode & Easy View
Canvas Coordinate System
The relation-graph canvas uses a transformation-based coordinate system consisting of two primary components:
| Property | Type | Description |
|---|---|---|
canvasOffset |
{x: number, y: number} |
Pan offset in pixels, represents the translation of the entire graph |
canvasZoom |
number |
Zoom level as a percentage (100 = 100%, 50 = 50%, etc.) |
These properties are stored in RGOptions and accessed through the data provider. All view control operations ultimately modify these values to transform the canvas.
Coordinate transformation:
displayX = (nodeX * zoomScale) + canvasOffset.x
displayY = (nodeY * zoomScale) + canvasOffset.y
where zoomScale = canvasZoom / 100
Zoom System Architecture
The zoom system provides two primary methods for zoom operations:
graph TB
subgraph "Public API"
zoom["zoom(buff, userZoomCenter?, e?)"]
setZoom["setZoom(finalZoom, userZoomCenter?)"]
end
subgraph "Zoom Calculation"
bounds["Check min/max bounds
(minCanvasZoom, maxCanvasZoom)"]
event["Emit beforeZoomStart event"]
scale["Calculate new scale
oldScale + (buff/100)"]
offset["Adjust canvasOffset
to maintain zoom center"]
end
subgraph "Side Effects"
perfHook["_performanceModeLogicHook"]
editView["_updateEditingControllerView"]
dataUpdate["_dataUpdated"]
zoomEvent["Emit onZoomEnd event"]
end
subgraph "Performance Mode Logic"
checkThreshold{"newZoom <= 40?"}
showEasy["Set showEasyView = true"]
hideEasy["Set showEasyView = false"]
updateLines["updateElementLines"]
end
zoom --> event
setZoom --> zoom
event --> bounds
bounds --> scale
scale --> offset
offset --> perfHook
perfHook --> checkThreshold
checkThreshold -->|Yes| showEasy
checkThreshold -->|No| hideEasy
hideEasy --> updateLines
perfHook --> editView
editView --> dataUpdate
dataUpdate --> zoomEventDiagram: Zoom Operation Flow
zoom() Method
The zoom() method applies a relative zoom adjustment:
-
Parameters:
buff: Zoom delta in percentage points (e.g., 10 for +10%, -10 for -10%)userZoomCenter: Optional center point for zoom in client coordinatese: Optional wheel event object
-
Behavior:
- Emits
beforeZoomStartevent (can abort if returnstrue) - Clamps zoom to
minCanvasZoomandmaxCanvasZoombounds - Calculates new scale:
newScale = (canvasZoom + buff) / 100 - Adjusts canvas offset to maintain zoom center position
- Updates
canvasOffsetandcanvasZoomin options - Calls performance mode logic hook
- Emits
onZoomEndevent with new and old zoom values
- Emits
Zoom center calculation:
mouseX = userZoomCenter.x - viewRectBox.left
mouseY = userZoomCenter.y - viewRectBox.top
newX = mouseX - (mouseX - canvasOffset.x) * (newScale / oldScale)
newY = mouseY - (mouseY - canvasOffset.y) * (newScale / oldScale)
This formula ensures that the point under the mouse cursor (or specified center) remains stationary during zoom.
setZoom() Method
The setZoom() method sets an absolute zoom level:
-
Parameters:
finalZoom: Target zoom level as percentage (e.g., 100 for 100%)userZoomCenter: Optional center point for zoom
-
Implementation: Calculates the delta from current zoom and calls
zoom():const buff = Math.round(finalZoom - options.canvasZoom); this.zoom(buff, userZoomCenter);
Performance Mode Integration
The zoom system integrates with performance mode through the _performanceModeLogicHook() method, which automatically switches rendering modes based on zoom level:
| Zoom Level | Rendering Mode | Behavior |
|---|---|---|
| > 40% | Standard SVG | Full SVG rendering with all details |
| ≤ 40% | Easy View (Canvas) | Simplified canvas-based rendering for performance |
Logic flow:
graph LR
zoomChange["Zoom Change"]
oldCheck{"oldZoom <= 40?"}
newCheck1{"newZoom > 40?"}
newCheck2{"newZoom <= 40?"}
hideEasy["showEasyView = false
updateElementLines()"]
showEasy["showEasyView = true"]
noChange["No change"]
zoomChange --> oldCheck
oldCheck -->|Yes| newCheck1
oldCheck -->|No| newCheck2
newCheck1 -->|Yes| hideEasy
newCheck1 -->|No| noChange
newCheck2 -->|Yes| showEasy
newCheck2 -->|No| noChangeDiagram: Performance Mode Switching Logic
The 40% threshold provides a balance between visual quality and rendering performance. When zooming out to view large graphs, the system automatically switches to canvas-based rendering see 6.3.
Pan Operations
Pan operations modify the canvasOffset property to move the visible area of the graph. The primary method is setCanvasCenter():
setCanvasCenter()
Moves a specific canvas coordinate to the center of the viewport:
setCanvasCenter(x: number, y: number) {
this.dataProvider.setCanvasCenter(x, y);
this._dataUpdated();
}
The data provider calculates the required offset to center the given coordinates:
canvasOffset.x = (viewSize.width / 2) - (x * zoomScale)
canvasOffset.y = (viewSize.height / 2) - (y * zoomScale)
Focus Operations
The RelationGraphWith6Effect class provides several high-level operations for focusing on specific nodes or content areas.
Focus Operation Methods
graph TB
subgraph "Public API"
focusById["focusNodeById(nodeId)"]
zoomToFit["zoomToFit(nodes?)"]
moveCenter["_moveToCenter(nodes?)"]
fitHeight["fitContentHeight(padding)"]
end
subgraph "Calculations"
getNode["getNodeById()
Retrieve node object"]
getRect["getNodesRectBox()
Calculate bounding box"]
getCenter["getNodesCenter()
Calculate center point"]
end
subgraph "Transform Operations"
setZoomOp["setZoom()"]
setCenterOp["setCanvasCenter()"]
setOffsetOp["setCanvasOffset()"]
end
focusById --> getNode
getNode --> focusNode["focusNode(thisNode)"]
focusNode --> setZoomOp
focusNode --> setOffsetOp
zoomToFit --> getRect
zoomToFit --> moveCenter
getRect --> calcZoom["Calculate zoom to fit"]
calcZoom --> setZoomOp
moveCenter --> getCenter
getCenter --> setCenterOp
fitHeight --> getRect
fitHeight --> setCenterOp
fitHeight --> setZoomOpDiagram: Focus Operation Methods and Dependencies
zoomToFit()
Automatically calculates the optimal zoom level and position to display specified nodes:
Algorithm:
- Calculate bounding box of target nodes using
getNodesRectBox() - Calculate zoom ratios for both dimensions with padding:
zoomPercentX = viewWidth / (boxWidth + padding * 2) zoomPercentY = viewHeight / (boxHeight + padding * 2) - Use the smaller ratio (clamped to max 100%):
zoomPercent = Math.min(zoomPercentX, zoomPercentY, 1) - Center the nodes using
_moveToCenter() - Apply the calculated zoom using
setZoom()
Default behavior: If no nodes are specified, operates on all nodes in the graph.
focusNodeById()
Focuses on a specific node by its ID:
- Retrieves node using
getNodeById(nodeId) - If found, calls private
focusNode()method - Sets zoom to 100%
- Calculates offset to center the node in the viewport
- Updates
checkedNodeIdoption to mark the focused node
Calculation for centering:
finalX = -nodeX + (viewWidth / 2) - (nodeWidth / 2)
finalY = -nodeY + (viewHeight / 2) - (nodeHeight / 2)
fitContentHeight()
Adjusts the canvas height to exactly fit the content while maintaining width:
Process:
- Temporarily sets canvas opacity to 0.01 (nearly invisible)
- Calculates nodes bounding box
- Determines scale factor:
scale = min(1, viewWidth / nodesWidth) - Calculates new height:
newHeight = nodesHeight * scale + padding * 2 - Updates view size options
- After 200ms delay:
- Applies calculated zoom
- Centers content using
_moveToCenter() - Restores canvas opacity to 1
This method is useful for scenarios where you want the graph to be fully visible without scrolling, such as in embedded widgets or report generation.
Animation System
The relation-graph animation system provides two independent animation controls:
Node Position Animation
Controls smooth transitions when node positions change (e.g., during layout):
| Method | Effect |
|---|---|
enableNodeXYAnimation() |
Enables smooth position transitions for nodes |
disableNodeXYAnimation() |
Nodes jump directly to new positions |
Controlled by the enableNodeXYAnimation option in RGOptions. When enabled, CSS transitions or framework-specific animation mechanisms handle the smooth movement.
Canvas Transform Animation
Controls smooth transitions for canvas zoom and pan operations:
| Method | Effect |
|---|---|
enableCanvasAnimation() |
Enables smooth zoom/pan transitions |
disableCanvasAnimation() |
Canvas transforms apply instantly |
Controlled by the enableCanvasTransformAnimation option. When enabled, provides a smoother user experience during navigation but may impact performance on large graphs.
Effect Operation Integration
The following diagram shows how view control operations integrate with the broader system:
graph TB
subgraph "User/API Triggers"
userZoom["Mouse Wheel
Pinch Gesture"]
apiCall["API Calls
setZoom, zoomToFit,
focusNodeById"]
layoutDone["Layout Completion"]
end
subgraph "RelationGraphWith5Zoom"
zoomMethod["zoom()"]
setZoomMethod["setZoom()"]
end
subgraph "RelationGraphWith6Effect"
zoomToFitMethod["zoomToFit()"]
focusMethod["focusNodeById()"]
fitHeightMethod["fitContentHeight()"]
animationToggles["enable/disable
NodeXYAnimation
CanvasAnimation"]
end
subgraph "Data Provider"
updateOpts["updateOptions()
canvasZoom
canvasOffset
enableNodeXYAnimation
enableCanvasTransformAnimation"]
setCenterMethod["setCanvasCenter()"]
setOffsetMethod["setCanvasOffset()"]
end
subgraph "Reactive Update"
dataUpdated["_dataUpdated()"]
updateHook["updateViewHook()"]
frameworkReact["Framework Reactivity
Vue refs, React state"]
end
subgraph "View Rendering"
componentRerender["Component Re-render"]
canvasRedraw["Canvas/EasyView Update"]
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 --> canvasRedrawDiagram: View Control Integration Flow
All view control operations follow a consistent pattern:
- API method called (user interaction or programmatic)
- Transform calculations performed
- Options updated via data provider
_dataUpdated()triggers reactive update- Framework-specific rendering updates view
Key Implementation Details
Zoom Bounds Enforcement
The zoom system enforces configurable bounds to prevent extreme zoom levels:
minCanvasZoom: Minimum allowed zoom (default varies by framework)maxCanvasZoom: Maximum allowed zoom (default varies by framework)
When zoom() is called, the method automatically clamps the resulting zoom level:
if ((options.canvasZoom + buff) < options.minCanvasZoom) {
buff = options.minCanvasZoom - options.canvasZoom;
} else if ((options.canvasZoom + buff) > options.maxCanvasZoom) {
buff = options.maxCanvasZoom - options.canvasZoom;
}
If the clamped buff becomes 0, the operation is aborted.
Event Integration
View control operations emit events at key points:
| Event | Triggered By | Parameters | Cancellable |
|---|---|---|---|
beforeZoomStart |
zoom() |
(currentZoom, buff, wheelEvent) |
Yes (return true) |
onZoomEnd |
zoom() |
(newZoom, oldZoom) |
No |
The beforeZoomStart event can prevent zoom operations by returning true, allowing parent components to implement custom zoom restrictions.
View Rectangle Query
Multiple operations depend on getViewBoundingClientRect(), which retrieves the current viewport dimensions in client coordinates. This is essential for:
- Calculating zoom center points
- Determining canvas-to-client coordinate transformations
- Viewport-based visibility checks
The method queries the actual DOM element’s bounding client rect, ensuring accurate positioning even when the graph is embedded in complex layouts.
Summary
The view control and effects system provides:
- Zoom operations via
zoom()andsetZoom()with automatic center-point preservation - Pan operations through
setCanvasCenter()and direct offset manipulation - Focus operations including
zoomToFit(),focusNodeById(), andfitContentHeight() - Animation controls for both node positions and canvas transforms
- Performance mode integration that automatically switches rendering modes at zoom thresholds
- Event-driven architecture allowing custom behavior and cancellation
All operations are coordinated through the data provider and trigger reactive updates, ensuring consistent behavior across Vue 2/3, React, and Svelte implementations see 10.