Camera ​
Guide to camera control and animation.
Overview ​
The camera determines what part of the 3D scene is visible. Control it programmatically for automated tours, saved views, or responsive layouts.
Camera State ​
The camera state includes position, target, and up vector:
const state = graph.getCameraState();
// Returns:
// {
// position: { x: 0, y: 0, z: 100 },
// target: { x: 0, y: 0, z: 0 },
// up: { x: 0, y: 1, z: 0 }
// }- position: Where the camera is located
- target: What the camera is looking at
- up: Which direction is "up" for the camera
Setting Camera Position ​
Direct Position Setting ​
// Set camera position
graph.setCameraPosition({ x: 0, y: 0, z: 100 });
// Set what the camera looks at
graph.setCameraTarget({ x: 0, y: 0, z: 0 });Zoom to Fit ​
Automatically frame all nodes:
graph.zoomToFit();With padding:
graph.zoomToFit({ padding: 1.2 }); // 20% extra spaceFraming after a load ​
The element frames the graph on its own after every data load and layout change. To leave the camera where the reader put it, switch that off:
element.autoFrame = false; // a load or layout change no longer moves the camera
element.autoFrame = true; // back to framing each load (the default)<graphty-element auto-frame="false"></graphty-element>zoomToFit() still frames the graph while it is off. The switch is a preference of the view: it records no undo step and is not saved in a project file. It is independent of startingCameraDistance, which also stops automatic framing but places the camera at a distance.
Zoom In and Out ​
Move the camera one step nearer or further, the way a zoom button does. The same call works on the 2D and the 3D camera:
await graph.zoomStep("in");
await graph.zoomStep("out");Zoom toward the pointer ​
By default the mouse wheel zooms toward the center of the view. Turn on zoomToPointer and it zooms toward the point under the pointer instead, so the node under the pointer stays under it:
element.zoomToPointer = true;<graphty-element zoom-to-pointer></graphty-element>Like autoFrame, it is a preference of the view: it records no undo step and is not saved in a project file.
Keys on the canvas ​
While the canvas has focus, keys move the camera. A key held repeats; a key tapped acts once. CANVAS_KEY_DESCRIPTORS from @graphty/graphty-element/catalog lists every key, what it does and in which view, so a shortcuts page can list them without keeping its own copy:
| View | Keys | Action |
|---|---|---|
| 3D | W, =, + | Zoom in |
| 3D | S, -, _ | Zoom out |
| 3D | Arrow keys | Turn |
| 3D | A, D | Roll |
| 2D | =, + | Zoom in |
| 2D | -, _ | Zoom out |
| 2D | W, A, S, D and the arrow keys | Pan |
| 2D | Q, E | Roll |
Zoom to Nodes ​
Frame one node or several, in 2D or 3D. Ids that name no node are skipped, and when none of them names a node the camera stays where it is:
await graph.zoomToNodes(["alice", "bob"], { animate: true });zoomToSelection() turns the camera to look at the middle of the selection, keeping where it stands. A selected edge counts by its two ends, so selecting only an edge frames that edge. With nothing selected the camera does not move.
None of these is an undoable step: the camera is view state, not part of a project.
Set Complete State ​
graph.setCameraState({
position: { x: 50, y: 50, z: 100 },
target: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 1, z: 0 },
});Camera Animation ​
Animate camera movements for smooth transitions:
const newState = {
position: { x: 100, y: 100, z: 200 },
target: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 1, z: 0 },
};
graph.setCameraState(newState, {
animate: true,
duration: 1000, // milliseconds
easing: "easeInOutQuad", // easing function
});Available easing functions:
lineareaseInQuad,easeOutQuad,easeInOutQuadeaseInCubic,easeOutCubic,easeInOutCubiceaseInExpo,easeOutExpo,easeInOutExpo
Camera Presets ​
Save and restore camera positions:
// Save current view
graph.saveCameraPreset("overview");
// Navigate to different part of graph
graph.setCameraPosition({ x: 200, y: 0, z: 50 });
// Restore saved view
graph.loadCameraPreset("overview");
// Restore with animation
graph.loadCameraPreset("overview", {
animate: true,
duration: 1000,
});Multiple Presets ​
// Save multiple presets
graph.saveCameraPreset("top");
graph.setCameraPosition({ x: 0, y: 100, z: 0 });
graph.setCameraTarget({ x: 0, y: 0, z: 0 });
graph.saveCameraPreset("side");
graph.setCameraPosition({ x: 100, y: 0, z: 0 });
graph.setCameraTarget({ x: 0, y: 0, z: 0 });
// Create a tour
const presets = ["overview", "top", "side"];
let current = 0;
function nextView() {
current = (current + 1) % presets.length;
graph.loadCameraPreset(presets[current], { animate: true });
}2D vs 3D Camera ​
3D Mode (Default) ​
Full 3D navigation with orbit controls:
- Mouse drag: Orbit around target
- Scroll: Zoom in/out
- Right-drag: Pan
2D Mode ​
Simplified camera for 2D layouts:
<graphty-element view-mode="2d"></graphty-element>// 2D camera only has x, y position
graph.setCameraPosition({ x: 0, y: 0, z: 100 });In 2D mode:
- Mouse drag: Pan
- Scroll: Zoom
- No orbit (camera always looks down at the plane)
Coordinate Transforms ​
Convert between world and screen coordinates:
World to Screen ​
// Convert 3D world position to 2D screen position
const worldPos = { x: 0, y: 0, z: 0 };
const screenPos = graph.worldToScreen(worldPos);
// Returns: { x: 400, y: 300 } (pixels from top-left)Use case: Position HTML overlays on nodes:
const node = graph.getNode("node1");
const screenPos = graph.worldToScreen(node.position);
overlay.style.left = `${screenPos.x}px`;
overlay.style.top = `${screenPos.y}px`;To place something over a node by its id, and to know whether the node is on screen, use nodeScreenPosition.
Screen to World ​
// Convert screen position to world position
const screenPos = { x: 400, y: 300 };
const worldPos = graph.screenToWorld(screenPos);
// Returns: { x: 0, y: 0, z: 0 }Camera Events ​
Listen to camera changes:
graph.on("camera-state-changed", ({ state }) => {
console.log("Camera moved:", state);
});Focus on Node ​
Animate the camera to frame a single node:
await graph.zoomToNodes(nodeId, { animate: true, duration: 500 });Orbit Animation ​
Create a rotating view:
function startOrbit() {
let angle = 0;
const radius = 100;
const center = { x: 0, y: 0, z: 0 };
const intervalId = setInterval(() => {
angle += 0.01;
graph.setCameraPosition({
x: center.x + radius * Math.cos(angle),
y: 20,
z: center.z + radius * Math.sin(angle),
});
graph.setCameraTarget(center);
}, 16); // ~60fps
return () => clearInterval(intervalId);
}
const stopOrbit = startOrbit();
// Later: stopOrbit();