NarraLeaf

镜头

整体平移、缩放并调色舞台的 `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 的全部变换方法: poszoomscalerotateopacitytransformfiltereffect 等,均作用于整个舞台

访问 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?: number
  • easing?: 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?: number
  • easing?: 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: CameraLensProps
  • options?: 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 层渲染在舞台之外,不受镜头移动影响,文本始终清晰可读

本页目录