跳转到内容

Cassiopeia 谱面嵌入

完整示例 chart.json引用的原创音频 original-tone.wav已提供。下载后保持这两个文件相邻;页面原创示例的简图由示例 rendererAdapter 绘制,使用游戏主题的入口见下文本站谱面。

mountChart 默认组合 Cassiopeia kernel、Vue ChartPlayer、Our Notes plugin、Three renderer 和 Web 音频/输入宿主。它拥有挂载 DOM、媒体、input、renderer 与数据 loader。自定义 renderer 可沿用 kernel/Web host 并呈现作者自己的图形。候选产物安装与 CDN 方法见 JavaScript 嵌入。

将完整 watch HTML另存为 index.html,再把 chart.json和original-tone.wav保存到同一个文件夹。JSON 是完整的八秒原创 ChartEmbedDocument,WAV 是音量较低的原创测试音。

my-chart/
index.html
chart.json
original-tone.wav

在 my-chart 文件夹中打开终端,运行 python3 -m http.server 8000(Windows 用 py -m http.server 8000),再打开 http://localhost:8000/。Python 安装和编辑器 HTTP 预览方法见快速开始。完成后在终端按 Ctrl+C 关闭服务。

点击加载我的 chart.json,等待已就绪,再点播放。你会看见原创示例的音符以默认游戏画面呈现,测试音提供实际媒体时钟。暂停停止播放,进度滑块按秒跳转。关闭播放器停止媒体并移除画面,之后可以重新加载。加载或播放失败会在画面上方显示原因,修正文件或连接后重试。

托管模块提供默认播放器和配套样式;模块与公开画面素材需要联网,自己的 JSON 和 WAV 则由本地 HTTP 提供。初始例子关闭按键音效,配乐仍正常播放。

在同一网页中保持歌曲 ID 100070、难度 expert,点击加载本站歌曲。等已就绪后点播放。source 读取当前歌曲,解析它返回的谱面文件,使用返回的配乐 URL。其他歌曲 ID 可以从API 入门示例取得;歌曲必须实际提供所选难度。换来源前先关闭当前播放器。

数据 source 和画面 theme 是两个独立 factory。完整 HTML 使用公开浏览器入口:

import { mountChart, haneokaChartSource, haneokaChartTheme } from "https://haneoka.org/embed/cassiopeia.js";
const chart = mountChart(container, {
source: haneokaChartSource({ songId: "100070", difficulty: "expert" }),
theme: haneokaChartTheme(), server: "intl", locale: "zh-CN", mode: "watch",
});
await chart.ready;
// 播放按钮中调用 await chart.play();组件关闭时 await chart.dispose()。

这段展示接入点;上面的完整 HTML 已提供 container、按钮、加载状态、取消、错误和清理。它使用轻量素材 quality 2,服务器与文本语言独立,无需账号或 release ID。

在完整 HTML 的 mountChart 参数中,把 volume: 0.3 改为 0–1 的音量,或把 rate: 1 改为 0.8,刷新网页后重新加载。mode: "watch" 在点击播放后自动演奏;mode: "play" 开启手动谱面输入。加入 settings: { noteSpeed: 9 } 可以调整滚动速度。后面的参数、skin、settings、source 和 handle 表保留完整公开默认值、范围和限制。自带 JSON 使用标准 ChartDocument;其他游戏的原始谱面应先通过其 parser/converter 转换。

后文包导入供已有打包应用参考,npm 发布另行进行。最初的普通 HTML 使用上述托管 ESM;打包应用单独 import @haneoka/embed-cassiopeia/style.css,官方 facade 会挂载匹配样式,并在最后一个实例销毁时移除。

ChartEmbedDocument 包含 chart,可选 audio、background、bgmOffsetMs 和 assets。chart 使用公开 ChartDocument 字段:version、bpmChanges、signatureChanges、timeScaleChanges、notes、lines、timeline、durationMs。选择 document 或 source 其中一个。

const handle = mountChart(element, {
document: { chart: myChartDocument, audio: "musique/été.wav", bgmOffsetMs: 250 },
assetsBase: "https://my.example/project/",
theme: haneokaChartTheme(),
locale: "fr-CA",
labels: { player: "Partition", pause: "Pause", loading: "Chargement" },
});
await handle.ready;

myChartDocument 是调用方实际准备的标准谱面。音频为时钟:seek(seconds) 使用媒体/呈现秒数,谱面时间减去 bgmOffsetMs,正偏移产生前导。没有音频时为暂停视觉预览。

HTTP 通过 httpDataSource({url,decode}),本地通过 fileDataSource(jsonFile) 与 fileResourceResolver(Map)。作者可使用单语言和 Unicode 素材名。原生 theme 自己还请求公开 API/素材;严格本地 Map 应让合法的公开绝对路径通过,或提供完整自带 theme。自带素材可用 document.assets: OurNotesAssetManifest 或 theme({loader,signal,skin}) factory。

const handle = mountChart(element, {
document: { chart: myChartDocument, audio: "audio.ogg" },
assetsBase: "https://my.example/project/",
rendererAdapter: {
async create({ element, chart, loader, signal }) {
const canvas = document.createElement("canvas");
element.append(canvas);
const context = canvas.getContext("2d");
const artwork = await loader.resourceUrl("notes.svg", { signal });
return {
render(snapshot, options) { drawMyChart(context, chart, snapshot, options, artwork); },
resize(width, height, ratio) { canvas.width = width * ratio; canvas.height = height * ratio; },
dispose() { canvas.remove(); },
};
},
},
});

drawMyChart 由作者实现。adapter 路径加载 kernel/Web host;自己的 render、resize、dispose 负责图形与 SE。可选 laneAtClientPoint(x,y) 返回 0–23 连续原生 lane,外部为 -1,以接收 play-mode input。createSession 为另一个源游戏提供自己的规则;默认使用当前 Cassiopeia session。

await chart.setOptions({ rate: 0.8, volume: 0.6, noteSoundVolume: 0.4, settings: { noteSpeed: 9 } });
await chart.setSkin({ noteSkin: "skin003", noteEffectSkin: "effect001Simple", noteSeGroup: 4 });
chart.seek(12);
await chart.play();
chart.pause();
await chart.dispose();

play、setOptions、setSkin、dispose 返回 Promise,pause/seek 同步。ready 表示视觉初始化,不表示媒体完整下载;在用户点击后调用 play 以解锁音频。默认 theme 更换皮肤时在当前媒体位置重新构建并暂停。skin001/002/003 为 Our Notes/Garupa/Honeycomb,effect001/effect001Simple 为效果版本,SE 组 1–4 为 Our Notes/Solid/Wood/Typing。

loadTimeoutMs 默认 30000,只覆盖构造。cancel 和外部 signal 终止 handle;dispose 幂等,加载中也能调用,拆除 DOM、停音频并释放 loader URL。重试先销毁再挂载。snapshot 提供 phase、playing、time、duration。

onEvent/subscribe 提供 load、state、player、error,load 保留 core 每个资源的进度,player 将既有播放器事件及参数转交宿主。subscribe 返回取消监听函数,监听错误与操作结果隔离。

assetsBase 解析作者数据资源;theme 的 apiBase/publicBase 独立指定公开 API/原素材根。core 默认 cors/omit,可配置 fetcher、headers、credentials 与每项默认 64 MiB 预算。默认播放器的媒体/贴图/SE 使用既有原生加载器及 anonymous CORS,未经过注入 fetcher/private headers;私人素材可由宿主供应可访问 URL 或 Blob。

把全部延迟 chunk 与模块一起发布,单独链接 style.css。本站图像消费在外站遇到 Referer 403 时,可让数据传输使用 new Request(request,{referrerPolicy:"no-referrer"});原生贴图/音频请求仍由其加载器控制,需按部署的 origin/加载方式确认访问。CSP 应允许模块、素材、媒体以及使用的 blob URL。

宿主沿用已有 Material 控件和加载状态,保持舞台尺寸及触控按钮。Cassiopeia 默认显示原图标与Haneoka链接,图标半透明悬浮,画面保持全尺寸,dispose自动清理;作者素材保持原许可。

默认八秒原创ChartDocument、轻音量WAV Blob和作者Canvas adapter,通过真实Cassiopeia session/MediaClock驱动。它展示作者集成,不冒充游戏皮肤。HTTP读取实际谱面JSON,file用你选的File;本站模式改用真实song source/native theme。修改options后加载,再play/pause/seek/setOptions,默认theme支持setSkin。

1. 选择谱面

从完整原创文件开始。加载后,你会看见示例画面,点击播放读第一句。

打开完整 JSON · 下载 JSON

正在准备示例…

高级参数与完整输入(修改后重新加载)
内容语言
加载记录与错误详情
查看页面示例代码

点击才import/create,同页另一个实例先dispose。input/publicSource/nativeTheme/url为示例字段,会转成真实factory;callback不能写JSON,实际adapter在源码链接中。五语显示另保留作者locale输入。

import { mountChart, haneokaChartSource, haneokaChartTheme } from "https://haneoka.org/embed/r-22bfe102f7deb73f/cassiopeia.js";
const chart = mountChart(element, { source: haneokaChartSource({songId:"100070",difficulty:"expert"}), theme:haneokaChartTheme(), server:"intl",locale:"en",mode:"watch" });
await chart.ready;
await chart.play(); // 用户按钮
await chart.dispose();

实际示例使用稳定官方alias。官方facade在mount注入原包CSS、最后dispose移除;包/bundler方式单独import style.css。r22没有nonce参数,后续nonce接口需取该版本真实receipt/interface。CDN文件为部署build输出,仓库保留源码。

使用本地音乐创作可编辑音符,见谱面创作器教程。其 Project JSON 和带音频备份与下面的播放 ChartDocument 分别使用对应入口。

container必填浏览器HTMLElement且有效尺寸,非法TypeError、同容器重复Error。runtime严格source/document二选一,否则TypeError。默认composition还需theme或document.assets,自带renderer管理自己的图形。

字段类型/默认作用/错误/组合
documentChartEmbedDocument,替代source必填notes/lines array和finite durationMs,非法ready TypeError。
sourceDataSource<ChartEmbedDocument>,替代document必填memory/http/file/custom,load失败拒绝ready。
serverstring/undefined,本站intlregistry,与locale独立。
localestring/en作者/UI语言。
assetsBase可选HTTP(S)目录/sourcebase无query/fragment,非法TypeError。
resolveResource(key,DataContext)=>URL/string/Blob/Promiseliteral键,默认原key;缺资源失败。
fetcher(Request)=>Promise<Response>,fetchcore/nativebytes;公开Haneoka assets/runtime/embed no-referrer,作者/API保留策略;音乐独立anonymous CORS。
credentialsomit/same-origin/include,omitcore传输,需要origin/credential允许。
headersHeadersInit,无core头,音乐不继承私有header。
maxBytes正safe integer/67108864每decodedresponse,非法RangeError;URL/媒体元素另计。
onListenerError(unknown)=>void,可选observer隔离。
theme(ChartThemeContext)=>manifest或Promise,可选默认呈现,优先于document.assets;factory失败拒绝construct,setSkin需要。
skinChartSkin/{}下表四字段;作者renderer不消费native皮肤。
rendererAdapterChartRendererAdapter,可选选kernel/Webhost作者呈现,自己拥有SE。
createSession(chart,playbackOptions)=>CassiopeiaSessionPort,可选作者renderer替代规则,默认ChartSession;不传给默认Vue driver。
signalAbortSignal,可选终止handle,ready后也适用。
loadTimeoutMs正safe integer/30000文档与每构造阶段期限,不是player生命周期;非法RangeError/超时TimeoutError。
labels{player?,pause?,loading?}可选string,默认Chart player/Pause/Loading,仅默认Vue。
onEvent(ChartEmbedEvent)=>void,可选加载前登记。
modechart/watch/play,watch预览/自动/手动input,变更沿driver reset/pause。
settingsPartial<RenderSettings>+judgementOffsetMs,{}下表完整;setOptions合并。
volumenumber/.80–1,初始media超界拒绝,更新clamp,需finite。
ratenumber/1正数规范化 .25–4;非法/非正用fallback。
loopboolean/false音乐loop。
noteSoundEnabledboolean/true默认SE,作者renderer自管SE。
noteSoundVolumenumber/.7默认SE gain,使用finite0–1。

core共同参数见core。无autoplay,ready后用户操作play;construct deadline按阶段,不表示ready已下载完整音乐。

skin取值/本站默认含义
noteSkinskin001/002/003,001Our Notes/Garupa/Honeycomb。
noteEffectSkineffect001/effect001Simple,前者native效果。
noteSeGroup1–4,1Our Notes/Solid/Wood/Typing。
currentQuality0/1/2,2high/middle/low素材,与graphicsQuality独立。

typed union由theme解析,坏/缺素材拒绝构造。labels.player/pause/loading全为可选string,不选择server或语言。

haneokaChartSource({songId,difficulty,apiBase?})的前两项必填,difficulty为easy/normal/hard/expert/special/master且该曲提供;apiBase 默认 https://haneoka.org/api/v1/。读取返回谱面file并parse/build,再用musicUrl;缺difficulty Error,data/budget/parser保留原因。server从mount。

haneokaChartTheme({apiBase?,publicBase?,server?}={}):API默认上述root,publicBase默认其origin根,server override默认loader.server→intl;与作者assetsBase独立。真实descriptor缺失/歧义Error。ChartThemeContext完整为loader、signal、skin;可以按signal读loader bytes并返回manifest。

以下是public OurNotes插件RenderSettings默认,mount本身只给partial{};固定旧SDK仍保留其plugin版本。未被mount显式校验的项原样传递,使用finite和建议区间,不伪称SDK逐项验证。

字段类型/默认呈现/规范化
noteSpeednumber/5scroll速度。
noteSpeedCurvenumber/1.30999994offset curve常量。
mirrorboolean/false原24lane镜像。
effectsboolean/truenote效果。
noteSizenumber/1.1–2 clamp。
longAlphanumber/.6.1–1。
guideAlphanumber/.6.1–1。
graphicsQualitynumber/1原0/1/2高/中/低。
guidelineCountnumber/6lane分隔。
guidelineOpacitynumber/.25建议0–1 alpha。
laneOpacitynumber/.8建议0–1。
showJudgementLineboolean/false判定线。
showSimultaneousLineboolean/true同时线。
showEaseNoteboolean/falseease/critical强调。
backgroundBrightnessnumber/.7建议0–1。
judgeResultPositioncenter/lane/none,center位置。
noteOverlapLaneBuffernumber/0判定lane buffer。
showFastSlowboolean/true时机提示。
showPerfectFastSlowboolean/true包括Perfect。
showJudgeOffsetMsboolean/falsems显示。
alwaysShowFastSlowboolean/false常驻偏好。
scoreRankScoresreadonly number[]/无C/B/A/S/SS阈值。
judgementOffsetMsnumber/0ms session offset,作者adapter用setOffset更新。

ChartEmbedDocument.chart必填;audio/background?:string默认无,资源key/URL;bgmOffsetMs?:number默认0,chartTime=mediaMs-offset;assets?:OurNotesAssetManifest在无theme时提供默认图形,rendererAdapter可省略;无audio为暂停预览。

ChartDocument root必填字段:version、bpmChanges、signatureChanges、timeScaleChanges、notes、lines、timeline、durationMs。BPM行tick/beat/timeMs/bpm;signature行tick/beat/timeMs/numerator/denominator;scale行timeMs/scale;timeline skills行index/tick/timeMs,fever行index/startTick/endTick/startTimeMs/endTimeMs,callChanges行index/tick/timeMs/rhythms。line为id/kind:long或guide/critical/noteIds。

note全部字段:id/tick/timeMs/beat/pos/size/laneX/width;operateType/judgementType/judgementAreaOffsetType/direction;critical/judged/visible;lineIds;slideAlong;indexInLine:number/null;easeL/easeR:enum/null。使用公共converter/enums,原创例子包含全部必填。

native manifest root为id、source{game,noteSkin,laneSkin,noteEffectSkin,authoredProfile}、liveQuality{quality,effectRenderingScale,backgroundCameraFps,live2dFps}、noteAtlas、arrowGradient?、lane、particles、slideLineStyle、hud、tmpSdfFont?、noteSeGroup、noteSounds、palette、tiltThresholds[{distance,value}]。它是provider DTO;sprite/particle/audio详细descriptor见完整公开schema,资源交给loader。

rendererAdapter.create({element,chart,loader,signal})同步/Promise返回render(snapshot,options)、resize(width,height,pixelRatio)、dispose()、可选laneAtClientPoint(clientX,clientY)。snapshot复用,只在当次消费;lane连续0–23或外部-1。作者adapter用真实MediaClock/input/session;createSession可以提供自己的publicsession规则,遵循reset/update/input/finish。

成员签名生命周期/错误
readyPromise<void>视觉准备,requiredSE失败拒绝,音频可还buffer。
snapshotphase/playing/time/durationphase为loading/ready/cancelled/error/disposed,秒。
play()=>Promise<void>需ready,返回浏览器audio拒绝。
pause()=>void需ready,state暂停。
seek(seconds)=>voidfinite非负否则RangeError,已知durationclamp,需ready。
setOptionsChartPlaybackOptions=>Promise<void>partialsettings合并,其它交driver。
setSkinChartSkin=>Promise<void>ready+默认theme factory,作者/no factory Error;当前位置重建并暂停。
subscribelistener=>unsubscribe未来事件,disposed Error。
cancel()=>voidboot/ready都终止,停止player/input/资源。
dispose共用Promise<void>幂等,包括loading;移DOM/fullscreen监听,停media/renderer/revoke;失败AggregateError。

events为load{event}、state{snapshot}、player{name,args}、error{error}。默认转发playing/media-playing/timeupdate/duration/judgement/skill/fever/callchange;timeupdate/duration为秒,judgement含note/judgement/timing/diffMs/judgedAtMs/combo/maxCombo/score/scoreDelta/life。observer异常由onListenerError隔离,source/transport/theme/decoder保留原Error,没有新的错误envelope。同容器重新mount前先dispose。

当前源码的署名背景透明,图标透明度55%,48px角落触控区域悬浮在完整画面上。稳定alias随匹配构建部署采用该行为;固定r22保留旧的预留行布局,显式固定旧版时以该版行为为准。