NarraLeaf

视觉效果 (Vfx)

用于雨、雪、花瓣等全屏循环视频叠加层的 `Vfx` 元素,支持预加载、淡入淡出与倍速控制

0.16.0 起可用

Vfx 以全屏循环视频叠加层的形式播放粒子与氛围效果(落樱、光尘、雨、雪、雾、光斑),无需 canvas 或 WebGL

叠加层渲染在场景与正在播放的 Video 之上、舞台摄像机边界之内:Camera 的平移/缩放/晃动会带着天气效果一起运动,而对话框 UI 保持固定

import {Vfx} from "narraleaf-react";

// 真 alpha 素材:任意背景下色彩保真(含暗色边缘的花瓣)
const petals = new Vfx({src: "/fx/petals-alpha.webm"});

// 黑底发光素材 + screen 混合:体积极小、可硬件解码
const dust = new Vfx({src: "/fx/dust-black.webm", blendMode: "screen", opacity: 0.9});

scene.action([
    petals.preload(),               // 现在就加载;屏幕上还没有东西
    dust.preload(),
    character`起风了。`,
    petals.show({duration: 800}),   // 淡入;动作会等待淡入完成
    dust.show({opacity: 0.4}),      // 仅本次显示生效
    character`花瓣飘下来了……`,
    petals.setPlaybackRate(0.5),    // 慢速飘落
    dust.pause(),                   // 冻结在当前帧
    dust.resume(),
    petals.hide({duration: 1200}),  // 淡出并停止播放
]);

素材路线

通过 blendMode 支持两条互补的素材路线:

路线素材blendMode取舍
真 alphaVP9 yuva420p alpha WebM"normal"(默认)任意背景色彩保真;体积较大,alpha 解码走软解
黑底发光VP9 yuv420p,效果绘制在黑底上"screen"体积小 5–10 倍、可硬件解码;加法混合会冲蚀暗色像素,只适合纯发光类效果

0.31.4 起,"normal" 以外的 blendMode 才真正生效。 在此之前,无论声明的是哪个值,叠加层 一律按 "normal" 合成,因此黑底素材会以一块不透明的矩形盖住场景,而不是把它的光叠加上去。构造参数没有变化, "normal" 的表现也一如既往

# 真 alpha 路线
ffmpeg -i frames_%04d.png -c:v libvpx-vp9 -pix_fmt yuva420p -auto-alt-ref 0 -b:v 0 -crf 34 fx-alpha.webm

# 黑底路线
ffmpeg -i frames_%04d.png -c:v libvpx-vp9 -pix_fmt yuv420p -b:v 0 -crf 34 fx-black.webm

含暗色或不透明像素的素材必须走 alpha 路线。循环素材应保证首尾帧一致,以获得无缝循环

行为

  • 玩家跳过(skip)时,show/hide 的淡变立即完成;素材加载失败会记录错误并立即完成动作,因此损坏的资源不会阻塞剧情
  • 舞台上的叠加层会被存档捕获,读档后直接重现,播放中或(若被暂停)冻结,不重放淡入。自 0.33.0 起预加载的叠加层同样在内,读档后保持隐藏。0.16.0 之前的旧存档可以正常读取
  • 存档中的叠加层若已不在剧情里,读档会跳过它并记录告警。其余元素仍会报错
  • 撤销(undo)会恢复叠加层此前的可见性

公共方法

constructor

  • config: Partial<VfxConfig> & {src: string} - VfxConfig
const rain = new Vfx({src: "/fx/rain-black.webm", blendMode: "screen"});

可链式方法

preload

0.33.0 起可用

将叠加层加入舞台但不显示。片段开始加载,动作立即完成

在显示它的前几行调用。show 会等待首帧就绪,预加载过的叠加层因此立即出现。对已在舞台上的叠加层调用不会有任何效果

scene.action([
    rain.preload(),
    character`天阴了一下午。`,
    rain.show({duration: 800}),
]);

show

将叠加层加入舞台,等待首帧就绪后淡入,并开始循环播放。动作会等待淡入完成。在已显示时调用是幂等的(从当前不透明度重新应用淡入)

0.33.0 起,options.opacityoptions.rate 仅对本次显示生效,缺省取叠加层自身的配置值,因此覆盖过一次之后的普通 show() 会回到配置值。两者都不进存档:读档后按配置的不透明度与速度播放

petals.show({duration: 800, easing: "easeOut"});
petals.show({opacity: 0.35, rate: 2});   // 仅本次:更淡、更快

hide

将叠加层淡出并停止播放。动作会等待淡出完成。在未显示时调用是 no-op

0.33.0 起,叠加层会留在舞台上,不可见也不播放;之后的 show 从停住的那一帧继续,而不是从头重放。只有新开游戏与读档会清空舞台

petals.hide({duration: 1200});

pause

将叠加层冻结在当前帧

dust.pause();

resume

从当前帧继续播放

dust.resume();

setPlaybackRate

  • rate: number - 播放速度(例如 0.5 表示慢速飘落)

调整播放速度。运行期的速度变更不会持久化;读档后速度回到 config.playbackRate

petals.setPlaybackRate(0.5);

本页目录