Skip to content

resource-service.ts — 资源服务

文件: web/src/services/resource-service.ts

职责

本地资源加载的统一入口,负责 Hanzi Writer 笔顺数据、字源 SVG、汉字音频的加载与播放。所有资源从本地 web/src/data/ 目录加载,无 CDN 依赖。

导出的函数

Hanzi Writer 笔顺数据

getHanziWriterData(char: string): Promise<any>

获取 Hanzi Writer 笔顺数据(本地 JSON)。

  • 参数: char — 汉字字符
  • 返回值: 笔顺数据对象,未找到时返回 null
  • 策略: LRU 缓存 → 请求去重 → import.meta.glob 查找

请求去重机制:同一汉字并发调用时共享同一个 Promise,避免重复加载。使用 _hwPending Map 管理进行中的请求,Promise 完成后自动清理。


字源 SVG

getEtymologySVGUrl(char: string, stage?: string): string

获取字源 SVG 本地 URL。

  • 参数: char — 汉字字符; stage — 书体阶段,默认 "oracle"(甲骨文)
  • 返回值: 本地路径字符串,格式 /data/etymology-svg/{stage}/{char}.svg

getEtymologySVGFallbackUrl(char: string, stage?: string): string

获取字源 SVG 回退 URL(当前版本无 CDN 回退,始终返回空字符串)。

  • 参数: char — 汉字字符; stage — 书体阶段
  • 返回值: 空字符串 ""

checkEtymologyAvailable(char: string, stage?: string): Promise<boolean>

预检查某个字在指定书体下是否有可用的字源 SVG(通过 Image 预加载,3s 超时)。

  • 参数: char — 汉字字符; stage — 书体阶段,默认 "oracle"
  • 返回值: Promise<boolean> — 图片加载成功返回 true,超时或失败返回 false

fetchEtymologyIndex(): Promise<Map<string, boolean> | null>

获取字源索引(index.json),用于快速判断字源 SVG 可用性。

  • 返回值: Map<string, boolean>,key 为 char|script 格式,value 为可用性;加载失败返回 null
  • 优先级: 内存缓存 → localStorage → public/data/etymology-svg/index.json

fetchEtymologyIndexRaw(): Promise<any>

获取字源索引原始 JSON 数据(供 character.vue 使用)。

  • 返回值: 完整的 index.json 数据(含 entriesevolutionStagesversion 等字段)

isEtymologyAvailableByIndex(char: string, script: string): boolean | null

通过索引缓存判断字源 SVG 可用性(零延迟,缓存未就绪时返回 null)。

  • 参数: char — 汉字字符; script — 书体脚本名
  • 返回值: true / false / null(缓存未就绪)

onEtymologySVGError(e: Event): void

处理 SVG 图片加载失败的回调(仅记录错误日志)。

  • 用法: <img :src="url" @error="onEtymologySVGError" />

isJsdelivrUrl(url: string): boolean

判断 URL 是否为 jsdelivr CDN 源(当前版本始终返回 false)。


汉字音频

getAudioUrl(pronunciation: string): string

获取汉字音频本地 URL。

  • 参数: pronunciation — 数字声调拼音,如 "yi1", "hao3"
  • 返回值: 本地路径字符串,格式 /data/hanzi-audio/{pronunciation}.mp3

getNextAudioUrl(currentUrl: string, pronunciation: string): string | null

获取下一个 CDN 音频 URL(当前版本无 CDN 回退,始终返回 null)。

playAudio(char: string, pronunciation: string): Promise<void>

播放汉字音频(本地 MP3 → TTS 降级)。

  • 参数: char — 汉字(用于 TTS 降级朗读); pronunciation — 数字声调拼音
  • 降级策略: 本地 MP3 加载失败 → 浏览器 TTS(SpeechSynthesis)

播放前会停止当前正在播放的音频。TTS 降级使用 zh-CN 语音,语速 0.8。

isAudioPlaying(): boolean

检查当前是否有音频正在播放。

onAudioEnded(callback: () => void): void

注册音频播放结束回调。


资源加载策略

资源类型策略
HanziWriter 笔顺数据本地 JSON(import.meta.glob)+ LRU 缓存 + 请求去重
字源 SVG本地 public/data/etymology-svg/
字源索引localStorage → 内存缓存 → public/data/etymology-svg/index.json
汉字音频本地 public/data/hanzi-audio/ → TTS 降级

使用示例

typescript
import { getHanziWriterData, getEtymologySVGUrl, playAudio, fetchEtymologyIndex } from "@/services/resource-service";

// 获取笔顺数据
const writerData = await getHanziWriterData("好");

// 获取字源 SVG URL
const svgUrl = getEtymologySVGUrl("好", "oracle");

// 检查字源可用性
const available = await checkEtymologyAvailable("好", "oracle");

// 获取字源索引
const index = await fetchEtymologyIndex();

// 播放汉字音频
await playAudio("好", "hao3");

// 监听播放结束
import { onAudioEnded } from "@/services/resource-service";
onAudioEnded(() => console.log("播放结束"));