0%

2026-07-12-live2d-项目日志

Live2D 看板娘项目日志

项目本地路径: D:\my-blog\technical
本地预览: http://127.0.0.1:8080(需先启动 Python HTTP Server 在 public/ 目录)
预览启动: cd D:\my-blog\technical\public && python -m http.server 8080
重新生成: cd D:\my-blog\technical && npx hexo generate


项目背景

  • Hexo 博客 + NexT.Muse 主题
  • 目标:左下角 Live2D 看板娘,三个角色轮换(小埋/和泉纱雾/香风智乃),点击触发语音(7s 冷却),各自独立语音不串扰

技术栈

  • Live2D 引擎: live2d-widgets v1.0.1 (stevenjoezhang/live2d-widget)
  • Runtime: Cubism 2.1 (live2d.min.js)
  • 模型源: [email] (CDN)
  • 语音: 小埋 34 句 wav(CDN 原声),纱雾 24 句 wav(B站视频 BV1fK4y1774f 提取,本地托管)
  • 语音识别: OpenAI Whisper small 模型(日语)
  • 注入点: source/_data/body-end.swig(PJAX 环境)
  • 核心配置: source/js/live2d-config.json / source/js/model_list.json
  • 模型目录: source/js/model/umaru/ source/js/model/sagiri/ source/js/model/chino/

成功的部分

事项 状态 说明
博客本地预览 Python HTTP server on 8080
菜单调整 home / categories / archives / music / vibe-coding
鼠标拖尾粒子 Canvas 蓝紫色粒子,PJAX 兼容
Live2D 渲染 PixiJS + Cubism2 核心加载成功
B站视频下载+提取 yt-dlp → 94秒 m4a → ffmpeg → WAV
Whisper 语音识别 small 模型,24 句日语,按标点分段
模型文件本地托管 umaru 34句 / sagiri 24句 / chino 静态
MSR 唱片音量 降至 30%
看板娘尺寸 scale 0.42,工具栏 0.7

反复失败的部分

问题 尝试次数 根因
CDN 路径 vs 本地路径 5+ 次来回切换 Widget 的 cdnPath 同时控制 model_list.json 位置和模型文件/语音路径解析,无法分离
本地 cdnPath 时语音 404 3 次 Widget 对 voice 文件的路径解析与 CDN 模式不同,本地模式下 voice/ 路径拼接错误
CDN cdnPath 时模型过多 3 次 只能加载 yzs 完整模型列表(Pio 80套 + Tia 60套 + 7个角色),fetch 拦截可过滤但副作用未知
fetch 拦截方案 当前 拦截 model_list.json 过滤三人 + 拦截 sagiri voice 重定向本地。但 widget 内部音频管理不隔离模型
小埋语音串到纱雾 未解决 Widget 切换模型时不清理上一个模型的 Audio 实例,导致语音残留
重复触发语音 未解决 Widget 的 tap motion 系统无内置冷却,且同 motion 重复触发依赖 widget 内部状态机
hitTest 报错 3 次出现 index.js:194 — widget v1.0.1 的 Cubism2 鼠标跟踪 bug,部分模型的 hit_areas 参数 ID 不匹配

当前架构

1
2
3
4
5
6
7
Widget (live2d-widgets v1.0.1)
├─ cdnPath: yzs CDN
├─ model_list.json → fetch 拦截 → 只返回 umaru/sagiri/chino
├─ umaru/index.json → CDN 直连(34 句原声)
├─ sagiri/index.json → fetch 拦截 → 本地版本(24 句 B 站语音)
├─ sagiri/voice/*.wav → fetch 拦截 → 本地文件
└─ 其他资源 → CDN 直连

无法解决的根本矛盾

  1. Widget 不支持模型间音频隔离 — 所有模型共享同一个 Audio 上下文,切换模型不清洗
  2. v1.0.1 的 Cubism2 路径解析 — voice 文件路径拼接逻辑在 CDN 和本地模式下不同,无法用单配置兼容
  3. 无内置冷却 — tap 触发依赖 widget 内部 motion 状态机,外部 JS 拦截会导致动画+语音一起失效
  4. hit_areas 参数 ID — 部分模型(umaru/chino)的 D_REF.PT_HEAD 等引用在 runtime 中为 null

已尝试过、确认无效的方案

  1. cdnPath="/js/" + 本地完整模型文件 → 语音 404
  2. cdnPath=yzs CDN → 模型列表无法过滤
  3. initWidget({ models: [...] }) 直接传模型 → 参数被忽略
  4. waifuPath 本地 JSON → 模型部分被 cdnPath/model_list.json 覆盖
  5. new Audio() 构造函数拦截 → 影响全局含 MSR 音乐播放器
  6. click 事件 capture 拦截 → 动画和语音一起被阻断

关键文件清单

文件 作用
D:\my-blog\technical\source\_data\body-end.swig 主注入点(Live2D widget + 鼠标拖尾 + 音量控制 + fetch 拦截)
D:\my-blog\technical\source\js\live2d-config.json 看板娘 tips/对话配置
D:\my-blog\technical\source\js\model_list.json 本地模型过滤列表(CDN 模式下未被使用)
D:\my-blog\technical\source\js\model\umaru\ 小埋模型文件(34 语音 + 34 动作)
D:\my-blog\technical\source\js\model\sagiri\ 纱雾模型文件(24 语音 + 动作)
D:\my-blog\technical\source\js\model\chino\ 智乃模型文件(无语音)
D:\my-blog\technical\themes\next\_config.yml NexT 主题配置(菜单、侧边栏、scheme)
D:\my-blog\technical\source\music\index.md 黑胶唱片播放器页面
D:\my-blog\technical\source\_data\styles.styl 自定义 CSS 注入点
D:\my-blog\technical\segment_audio.py 音频分段脚本(Whisper + ffmpeg)
D:\my-blog\technical\sagiri_voice.m4a B 站下载的原始音频
D:\my-blog\technical\sagiri_voice.wav 转换后的 WAV(16kHz mono)
D:\my-blog\technical\sagiri_segments\ 24 个分段音频 + metadata.json