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 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();