Skip to content

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:

typescript
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 ​

typescript
// 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:

typescript
graph.zoomToFit();

With padding:

typescript
graph.zoomToFit({ padding: 1.2 }); // 20% extra space

Framing 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:

typescript
element.autoFrame = false; // a load or layout change no longer moves the camera
element.autoFrame = true; // back to framing each load (the default)
html
<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:

typescript
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:

typescript
element.zoomToPointer = true;
html
<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:

ViewKeysAction
3DW, =, +Zoom in
3DS, -, _Zoom out
3DArrow keysTurn
3DA, DRoll
2D=, +Zoom in
2D-, _Zoom out
2DW, A, S, D and the arrow keysPan
2DQ, ERoll

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:

typescript
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 ​

typescript
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:

typescript
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:

  • linear
  • easeInQuad, easeOutQuad, easeInOutQuad
  • easeInCubic, easeOutCubic, easeInOutCubic
  • easeInExpo, easeOutExpo, easeInOutExpo

Camera Presets ​

Save and restore camera positions:

typescript
// 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 ​

typescript
// 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:

html
<graphty-element view-mode="2d"></graphty-element>
typescript
// 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 ​

typescript
// 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:

typescript
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 ​

typescript
// 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:

typescript
graph.on("camera-state-changed", ({ state }) => {
    console.log("Camera moved:", state);
});

Focus on Node ​

Animate the camera to frame a single node:

typescript
await graph.zoomToNodes(nodeId, { animate: true, duration: 500 });

Orbit Animation ​

Create a rotating view:

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

Interactive Examples ​