Camera
This class extends Displayble
The camera transforms the whole stage as one unit: every Scene, its backgrounds and sprites, and any playing Video move together. The dialog box, menus, and NVL layer stay fixed.
Every Story has exactly one camera, reachable as story.camera. You author camera actions like any other element action.
import { Story, Scene, Character } from "narraleaf-react";
const story = new Story("entry");
const scene = new Scene("scene 1");
const aria = new Character("Aria");
scene.action([
story.camera.zoom(2, 800, "easeInOut"), // zoom the whole stage in
story.camera.pan({ xalign: 0.3 }, 800), // slide the view across
story.camera.rotate(3, 400), // tilt
story.camera.darken(0.6, 500), // dim the stage
aria.say`It's getting dark...`,
story.camera.resetCamera(600), // return to the neutral pose
]);The camera reuses the same Transform pipeline as
images and layers, so it inherits every chainable transform method from
Displayable: pos, zoom, scale, rotate,
opacity, transform, filter, effect, and more. They all apply to the whole stage.
Accessing the camera
story.camera is a single, always-present camera. There is exactly one per story, its pose persists across scene changes, and it is captured by save/load like any other element.
scene.action([
story.camera.zoom(1.5, 600),
story.camera.darken(0.4, 400),
]);Public Methods
pan
Pan the camera so the given position sits at the centre of the view. This is an alias of the inherited pos method with camera-oriented naming.
story.camera.pan({ xalign: 0.3 }, 800, "easeInOut");darken
Darken the whole stage. darkness is a value between 0 (normal) and 1 (black). Under the hood it drives the camera's CSS filter to brightness(1 - darkness), the same mechanism as Image.darken.
story.camera.darken(0.6, 500, "easeInOut");Darken shares the single CSS filter channel, so it overwrites (and is overwritten by) other
filters. To combine darken with another filter such as blur, write the full string yourself
via camera.filter("blur(4px) brightness(0.4)").
resetCamera
Return the camera to its neutral pose: centred, zoom 1, no rotation, fully opaque, and no filter (which also clears darken).
story.camera.resetCamera(600, "easeInOut");The filter is cleared on the first frame, and the pose eases over the duration given.
Give no duration when a grade is replaced. A filter eased from one chain to another takes the stage through the colors between them.
Renamed in 0.26.0: this was camera.reset. Every element carries an internal reset() —
the lifecycle hook the engine runs over the cast when a new game starts or a save loads — and
the camera was spending that name on this authoring helper, so newGame() never restored the
camera's pose and a story that panned or zoomed kept that framing into the next playthrough.
Rename any camera.reset(...) in your script. Calling reset() on a camera now reaches the
lifecycle hook instead: it restores the configured pose at once, animates nothing, and returns
the camera rather than a chainable action.
Inherited transform methods
The camera is a Displayable, so its full transform surface moves the whole stage:
zoom(factor, duration?, easing?)— zoom in/out uniformly.scale(scaleX, scaleY, duration?, easing?)— non-uniform scale.rotate(deg, duration?, easing?)— rotate the view.pos(position, duration?, easing?)— pan (seepan).opacity(value, duration?, easing?)— fade the whole stage.filter(css, options?)/effect(...)— colour-grade the stage (e.g."contrast(1.2) saturate(1.3)").transform(new Transform(...))— run a full multi-step Transform sequence.
Setting the initial pose
There is exactly one camera per story. Pass your own only to set its starting pose:
import { Story, Camera } from "narraleaf-react";
const story = new Story("entry", {
camera: new Camera({ zoom: 1.2 }),
});The camera moves the visual stage only. The dialog box, menus, and the NVL layer are rendered outside it and are intentionally unaffected, so text stays readable while the camera moves.