镜头
整体平移、缩放并调色舞台的 `Camera` 元素,另含快门、暗角与镜头效果
该类继承自 Displayable
Camera 对整个舞台进行变换:每个 Scene、背景、立绘和 Video 会一起运动。对话框、菜单和 NVL 层保持固定
每个 Story 恰好拥有一个 Camera,通过 story.camera 访问
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"), // 放大
story.camera.pan({ xalign: 0.3 }, 800), // 平移
story.camera.rotate(3, 400), // 旋转
story.camera.darken(0.6, 500), // 压暗
aria.say`天色渐暗……`,
story.camera.resetCamera(600), // 重置
]);Camera 复用与 Image、Layer 相同的 Transform 管线,
继承了 Displayable 的全部变换方法:
pos、zoom、scale、rotate、opacity、transform、filter、effect 等,均作用于整个舞台
访问 Camera
story.camera 是全局唯一的 Camera。每个故事只有一个,其姿态在场景切换间保持不变,并随存档/读档保存与恢复
scene.action([
story.camera.zoom(1.5, 600),
story.camera.darken(0.4, 400),
]);公共方法
pan
平移镜头,使指定位置居中。是 pos 方法的语义化别名:
story.camera.pan({ xalign: 0.3 }, 800, "easeInOut");darken
压暗整个舞台。darkness 取值 0(正常)到 1(全黑)。底层将 CSS filter 设为 brightness(1 - darkness),与 Image.darken 机制相同:
story.camera.darken(0.6, 500, "easeInOut");darken 与其他滤镜共用 CSS filter 通道,会相互覆盖。若需组合使用,请手动写出完整滤镜字符串,例如 camera.filter("blur(4px) brightness(0.4)")
shutter
自
0.31.0起可用
开合快门:两片叶片从画面上下向中间合拢。
1 是完全合上,0 是完全打开,中间的值是部分遮挡 —— 所以一个长期保持的小值是宽银幕而不是眨眼,0.12 大约就是电影感的遮幅。眨眼就是把它推到 1 再收回来;一次眨眼的节奏属于故事,所以引擎只提供这条通道而不提供一个命名好的套路。
shutter: number- 遮挡程度,0(开)到1(合)。越界值会被截断duration?: numbereasing?: EasingDefinition
scene.action([
story.camera.shutter(1, 180, "easeInOut"),
story.camera.shutter(0, 220, "easeInOut"),
]);vignette
自
0.31.0起可用
压暗画面四角。
与 darken 不同,那个是盖在画面上的滤镜,而暗角是盖在视野上的一块板:它不跟着相机走,所以舞台在下面推拉、平移、旋转时,暗角是稳不动的。它的行法用 lens 调。
vignette: number- 强度,0(无)到1。越界值会被截断duration?: numbereasing?: EasingDefinition
scene.action([
story.camera.vignette(0.72, 300, "easeInOut"),
narrator.say`一切都收到了房间正中央。`,
story.camera.vignette(0, 300, "easeInOut"),
]);lens
自
0.31.0起可用
一次性设置镜头的各条通道 —— 两个强度,以及它们绘制时用的颜色与行法几何。
几何字段在它们所属的强度下一次大于 0 时生效,所以通常先一刀设好,再把效果淡入。
type CameraLensProps = {
shutter?: number; // 0 开,1 合。默认 0
shutterColor?: string; // 默认 "#000"
vignette?: number; // 0 无,1 边缘完全不透。默认 0
vignetteColor?: string; // 默认 "#000"
vignetteInner?: string; // 暗角开始的半径。默认 "44%"
vignetteOuter?: string; // 达到满强度的半径。默认 "78%"
};lens: CameraLensPropsoptions?: VisualEffectOptions
scene.action([
story.camera.lens({vignetteColor: "#1a0b2e", vignetteInner: "20%", vignetteOuter: "95%"}),
story.camera.vignette(0.9, 400),
]);resetCamera
将 Camera 恢复到初始状态:居中、缩放为 1、无旋转、完全不透明、无滤镜(包括清除 darken):
story.camera.resetCamera(600, "easeInOut");滤镜在第一帧清除,姿态在给定时长内缓动
更换调色时不要给时长。滤镜从一条链缓动到另一条链,会让舞台经过两者之间的颜色
0.26.0 重命名:它此前叫 camera.reset。 每个元素都自带一个内部的 reset() —— 新游戏开始或读档时引擎在全体元素上运行的生命周期钩子 —— 而 Camera 却把这个名字给了这个创作用的辅助方法,于是 newGame() 从不恢复镜头姿态,平移或缩放过的故事会把那个取景带进下一次游玩
请把剧本里所有 camera.reset(...) 改名。现在在 Camera 上调用 reset() 触及的是生命周期钩子:它立即恢复到构造配置描述的姿态,不做任何动画,返回的是 Camera 本身而不是可链式调用的动作
继承的变换方法
Camera 是 Displayable,全部变换作用于整个舞台:
zoom(factor, duration?, easing?)— 等比缩放scale(scaleX, scaleY, duration?, easing?)— 非等比缩放rotate(deg, duration?, easing?)— 旋转pos(position, duration?, easing?)— 平移(即pan)opacity(value, duration?, easing?)— 淡入/淡出filter(css, options?)/effect(...)— 调色(如"contrast(1.2) saturate(1.3)")transform(new Transform(...))— 多步 Transform 序列
设置初始姿态
每个故事只有一个 Camera。仅当需要自定义初始姿态时才需传入:
import { Story, Camera } from "narraleaf-react";
const story = new Story("entry", {
camera: new Camera({ zoom: 1.2 }),
});Camera 只移动视觉舞台。对话框、菜单和 NVL 层渲染在舞台之外,不受镜头移动影响,文本始终清晰可读