跳转到内容

数据源与资源加载

Core 读取数据、解析素材路径并管理加载与清理。它保留原数据格式;Vega、Cassiopeia、Home Spot 分别负责自己的画面。本页先读取一个真实 JSON 和图片,剧情播放见 Vega 教程。浏览器使用已托管的 https://haneoka.org/embed/core.js,无需安装 npm。

打开完整 promenade.json。它保存标题、背景路径和一行文字;garden.svg是对应的原创图片。下面的页内示例选择「HTTP JSON」并加载,即可看见数据。随后点「解析资源 URL」取得图片地址,或「读取资源字节」检查实际内容。这里读到的 lines 数据不是 Vega 可播放文档,转换见第 3 步。

修改 JSON 参数,然后点击加载。模块和请求只在点击后启动;输出包含真实响应与传输记录。

语言

正在准备示例…

查看实际示例代码
import { createEmbedLoader, httpDataSource } from "https://haneoka.org/embed/core.js";
const loader = createEmbedLoader({
source: httpDataSource({ url: "https://docs.haneoka.org/examples/promenade.json" }),
locale: "fr",
});
const data = await loader.load();
const imageUrl = await loader.resourceUrl(data.background);
// 消费者使用完这个地址后,再 await loader.dispose()。

HTTP 相对素材以配置的 JSON 所在目录为基准,重定向不改变这个基准;显式 assetsBase 可覆盖它。resourceUrl 取得地址,resourceBytes 读取实际字节。source/locale/resourceKey 等示例面板字段会转换成实际 factory,其中 resourceKey/resourceField 是此演示选择素材的字段,完整 SDK 参数见后面的表。

分别保存 promenade.json 与 garden.svg。打开完整文件读取网页,先选择 JSON,再选择图片,点击「加载所选文件」。你会看见标题 Une promenade、原创庭园图片及实际图片字节数。关闭读取器会先移除图片,再销毁 loader 和它的 Blob URL;可以重新选择文件再加载。

将下面代码保存为 UTF-8 的 index.html 即可保留这个完整项目;本地 HTTP 打开方法见快速开始。目录为 index.html、promenade.json、garden.svg 三个文件。JSON 中 background 是素材键,文件映射以这个键指向你实际选择的 File,也支持合法 Unicode 路径。

分别选择 JSON 和所需素材。这里映射一张图片;多文件项目为每个声明的素材键及模型清单引用文件提供对应的 File。

3. 把本页的通用数据转换为剧情

Section titled “3. 把本页的通用数据转换为剧情”

promenade.json 的格式是本页的数据读取例,不是 AdvStory。Vega 要求 commands 数组。对这份明确的 lines 格式,可以在读取 data 后创建:

const storyDocument = {
localization: { locales: [data.language], defaultLocale: data.language },
commands: [
{ command: 25, background: { url: data.background } },
...data.lines.map(line => ({ command: 2, targetName: line.speaker, text: line.text })),
],
};

把 storyDocument 作为 mountStory 的 document,并把 assetsBase 指向同一素材目录;完整可保存剧情网页与加载步骤在 Vega。选择的 JSON 已是完整 AdvStory 时,直接读取它,不再做这个特定 lines 转换。decode 校验/转换实际数据,TypeScript 泛型只描述期望类型。

在页内示例选「本站资料」,加载公开歌曲后解析 jacketUrl。适配器与消费者均可使用托管入口:

import { createEmbedLoader, haneokaDataSource } from "https://haneoka.org/embed/core.js";
const loader = createEmbedLoader({
source: haneokaDataSource({ resource: "songs", id: "100070" }),
server: "intl", locale: "en",
});
const song = await loader.load();
const imageUrl = await loader.resourceUrl(song.jacketUrl);
// 先结束消费者,再 await loader.dispose()。

公开读取无需账号或 release。server 选择素材目录,locale 选择语言上下文,返回资料保留源提供的多语字段。source.assetsBase 默认是 API origin;资源基于它解析,显式 loader.assetsBase 可覆盖。repeat load 会再次读取当前资料。自有对象使用 memoryDataSource,选中的 File 使用 fileDataSource,更多签名和传输限制都在下方完整参考。


字段类型/必填/默认作用、组合和错误
source必填 DataSource<T>提供 load(context),每次 load 调用;可选 source.assetsBase 为素材目录,dispose 清理一次。数据格式应符合消费宿主。
server可选 string,undefined原样传给 source/resolver;本站 adapter 默认intl并用公开客户端校验 slug,与语言独立。
locale可选 string,en原样保留 fr/pt-BR 等作者语言;本站通过 Accept-Language 协商支持语言。
assetsBase可选 string,source.assetsBase→无覆盖 source base,绝对 HTTP(S) 目录,不含 query/fragment,补斜线;构造时非法 TypeError,无base相对key URL构造失败。
resolveResource可选 (key,DataContext)=>string/URL/Blob 或其Promise,默认原key完整映射key,相对结果基于base;拒绝即资源失败,Map缺key不回落网络。
fetcher可选 (Request)=>Promise<Response>,默认fetch接收cors、配置headers/credentials/signal的Request,文档和资源共同使用;媒体元素有独立加载器。拒绝传递为操作失败。
credentialsomit/same-origin/include,omit跨域带cookie需服务允许origin与credentials;非法值由Request拒绝。
headers可选 HeadersInit,无共用头,source逐次header覆盖同名值;本站以locale设Accept-Language,校验遵循Fetch。
maxBytes可选正safe integer,67108864每项解码JSON/资源bytes预算,不是总场景/cache/URL预算。构造非法RangeError,超预算read RangeError(本站包装response_too_large)。
onListenerError可选 (unknown)=>void,忽略接收observer异常,自身异常隔离,不影响操作结果。

构造无signal/timeout/decode/自动重试cache选项;使用逐次signal、source decoder和宿主策略。resourceUrl仅解析,不下载,也不约束之后媒体元素的字节量。

Factory必填可选/默认路径与校验
memoryDataSource(data)任意T无返回同一值/对象identity,不复制校验,无默认素材目录。
httpDataSource({url,decode?})绝对HTTP(S) string/URLdecode(unknown)=>T,默认JSONfactory校验URL/protocol,base为配置URL目录;redirect不改变base。读取遵循signal/budget,JSON/decoder失败拒绝。
fileDataSource(file,decode?)File或Blobdecoder同上无网络base,实际Blob bytes受预算/进度控制,坏JSON/decoder拒绝。
fileResourceResolver(files)ReadonlyMap<string,Blob>无Unicode完整key字面查找,不规范化;缺项Error。URL生成owned BlobURL,bytes直接读Blob。
haneokaDataSource(options)resource:string下述字段当前公开目录,不填账号/release。

本站source全部字段:resource 使用manifest资源语法;id?:string 有值取entity,省略取index;view?:string 选manifest投影;apiBase?:string 默认 https://haneoka.org/api/v1/,绝对HTTP(S)目录不含query/fragment;decode? 校验index/entity,默认JSON值。语法/不存在等load错误沿客户端。source默认素材base是API的origin根,可由loader.assetsBase覆盖;server/locale来自loader。

自定义 source/resolver 的完整 context

Section titled “自定义 source/resolver 的完整 context”

DataSource<T> 包含 load(context):T|Promise<T>、可选 assetsBase?:string、dispose():void|Promise<void>。loader销毁时清理一次并等待,cleanup失败拒绝dispose;拥有资源的source按单loader所有权使用。

DataContext字段类型含义
server可选string原样registry。
localestring作者语言或默认值。
signalAbortSignal当前操作取消,底层工作应跟随。
fetcher(Request)=>Promise<Response>配置后的传输。
maxBytesnumber有效解码预算,自定义解析也应遵守。
progress({loaded:number,total?:number})=>void实际字节,未知总量省略total。

Resolver同步/异步返回string、URL、Blob。绝对HTTP/HTTPS/Blob/data保留origin,相对基于assetsBase,/开头用origin根;其他protocol TypeError,不猜SSR/页面base。

const source = {
assetsBase: "https://author.example/project/",
async load(context) {
context.signal.throwIfAborted();
const response = await context.fetcher(new Request("https://author.example/project/story.json", { signal: context.signal }));
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json(); // 接入自己的格式校验和预算读取。
},
dispose() { /* 回收source拥有的资源 */ },
};
const loader = createEmbedLoader({ source, locale: "pt-BR", resolveResource: key => key });
成员参数/结果生命周期
localereadonly string有效语言。
serverreadonly string/undefined显式server,adapterfallback前。
disposedreadonly booleandispose开始即true。
load(options={})可选signal,Promise<T>每次独立source操作,key为$data。
resourceUrl(key,options={})必填string,可选signal;Promise<string>不下载,每个Blob复用一个URL直到销毁。
resourceBytes(key,options={})key/可选signal;Promise<Uint8Array>status/budget/progress真实读取。
subscribe(listener)必填(event)=>void;返回unsubscribe后续事件,不重放;已销毁抛Error。
cancel()void取消当前全部操作,loader可重用。
dispose()共用Promise<void>立即禁止新工作、abort、revoke、disposed事件、清监听、source清理一次;幂等,不能重用。

忽略signal的source也不能迟到ready/progress覆盖已取消操作;底层source仍应主动停止。cancel可继续,dispose结束loader。先销毁consumer,再撤销loader拥有的URL;作者自行创建的URL自行管理。

type字段时机
start/readyoperation:number,key:string开始/成功。
progressoperation/key/loaded:number/total?:number实际解码字节,内存或仅URL无自动bytes进度;压缩或未知length不填total。
error/cancelledoperation/key/error:unknown失败/取消。
disposed仅type销毁,销毁后的取消错误不继续发出。

operation按loader增长,key为$data或原资源名。observer异常不拒绝操作,并发独立、不自动重试或去重;演示只保留32条显示记录,不改变SDK事件流。

EmbedHttpError(status,url) 扩展Error,带name/message/status/url。URL/base/protocol非法TypeError,预算RangeError,JSON SyntaxError,作者source/resolver/decode原错误保留。本站保留ApiClientError,预算为response_too_large及HTTPstatus,默认不可重试。abort拒绝signal.reason,通常AbortError/TimeoutError。dispose后调用为Error,sourcecleanup失败拒绝dispose。

createHaneokaBranding(document,options={}) 在官方core facade和包/branding入口可用。document必填DOM Document;corner?:top-left/top-right/bottom-left/bottom-right,默认top-left。返回HTMLAnchorElement,不自动append,不请求图像。宿主在有定位的完整容器角落悬浮append,保留48px触控区域,清理时remove。链接固定为 Haneoka,_blank/noopener、原inlineSVG、48px触控与focus;可视embed自动管理,data-only core导入不碰DOM。