Files
zyt/TUICallKit-Vue3/training/static
longandClaude Opus 4.7 c3e4dfa479 feat(training): 握力环训练全面优化 - 音效集成 + 沉浸式画布 + Canvas 2D
## 主要改动

### 1. 音效集成
- 集成 4 个捏碎音效(鸡蛋/核桃/易拉罐/气球)
- 集成背景音乐 BGM + 右上角开关按钮
- 修复音效 URL 空值时的崩溃问题
- useMetronome 新增 volume 参数(默认 1.0)
- 节拍音量调小至 0.35,避免木鱼声过大

### 2. 画布升级到 Canvas 2D 接口
- 解决微信小程序 canvas-id 接口的警告
- 支持同层渲染,可与其他元素正常叠加
- 使用 requestAnimationFrame 替代 setTimeout
- DPR 自适应,Retina 屏幕高清渲染

### 3. 粒子特效差异化升级
- 4 种物品各自独立配色和形状
  - 🥚 鸡蛋: 蛋黄黄+白色,圆形,中速
  - 🌰 核桃: 4 种棕色,方形/三角/长方形,重粒
  - 🥫 易拉罐: 5 种银色,长方形,快速
  - 🎈 气球: 8 色彩虹,小颗粒,飘浮(低重力)
- 新增双层冲击波效果
- 粒子旋转动画(方形/三角形/长方形)
- 多形状粒子绘制(circle/square/triangle/rect)

### 4. 沉浸式画布重新设计
- 画布占屏幕 60vh(min 720rpx, max 900rpx)
- 背景与页面无缝融合(#f8fafc)
- 中心 1400rpx 大放射光晕,营造空间感
- 握力环放大至 520rpx,内圈 280rpx
- 完全移除浮动气泡(性能优化)

### 5. 视觉与体验优化
- 主题色改回青绿色(#14b8a6),握力环保持粉色突出
- 训练完成卡片大改造:
  - 大号 emoji 跳动动画
  - 统计项加入图标(💪⏱️🔥)
  - 食物对照可视化(emoji + 数量,最多 3 个)
  - 食物库扩展到 7 种(糖果/饼干/巧克力/苹果/香蕉/米饭/可乐)
- BPM 数字大号显示(80rpx 深粉色)
- 实时统计栏增加分隔线和顶部彩色高光

### 6. 性能优化(低配机器友好)
- 静态背景渐变(0 性能消耗)
- 单层中心光晕(无动画)
- 外发光仅在播放时启用
- GPU 加速(will-change + transform/opacity)
- 移除 backdrop-filter(高消耗)

### 7. Bug 修复
- crush-canvas 的 \$scope of null 错误
- 进入页面时音效预热不再发声(静音预热)
- 修复 walnut emoji 错用花生(🥜🌰)
- 弹窗遮挡粒子动画(延迟 1200ms 显示弹窗)

### 8. 文档
- 新增音效采集清单(grip-ring-audio-shopping-list.md)

## 修改文件
- grip-ring.vue: 主页面,音效集成、UI 重设计、食物对照
- grip-ring-canvas.vue: 沉浸式画布、握力环主体
- crush-canvas.vue: Canvas 2D 升级、差异化粒子系统
- useMetronome.ts: 音量参数支持
- dev-training-entry/index.vue: 入口配置微调

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 11:35:43 +08:00
..

训练模块静态资源

本目录用于存放"练一练"功能的所有音频素材。

目录结构

training/static/
├── audio/                      # 当前为空 —— 节拍器音频已全部迁移到 COS CDN
├── voice/                      # (规划中) TTS 语音教练
│   ├── numbers/                  数字报数 1~30
│   └── prompts/                  开始/休息/再来 等口令
├── bgm/                        # (规划中) 训练/休息背景乐
└── footprint.svg               # 脚印图标(早期方案残留,可保留作为备用素材)

节拍器音频已完全走 CDNgz-1349751149.cos.ap-guangzhou.myqcloud.com),减少小程序包体积约 210 KB。useMetronomeBg.ts / useMetronome.ts 中的 URL 即源信息,更换音色只需改 hook 里的常量。

准备步骤

1. 语音素材(小米 MiMo TTS 自动生成)

需要 Node 18+(用内置 fetch)。先准备小米 MiMo API Key

export MIMO_API_KEY=sk-your-key-here

cd uniapp
node scripts/generate-voice.mjs

会自动调用 mimo-v2.5-tts 模型生成 39 个 mp3(30 个数字 + 9 个口令)到 voice/ 目录。

可选参数

# 换音色(默认 冰糖;可选:冰糖/茉莉/苏打/白桦/Mia/Chloe/Milo/Dean
node scripts/generate-voice.mjs --voice 茉莉

# 换格式(默认 mp3,需要跟 hooks/useVoiceCoach.ts 里的 .mp3 后缀对应)
node scripts/generate-voice.mjs --format wav

# 强制重新生成(默认存在则跳过)
node scripts/generate-voice.mjs --force

音色推荐(中文女声更适合健身教练):

  • 冰糖:温柔甜美,亲和力强(默认)
  • 茉莉:清爽利落,有"运动博主"感
  • 苏打:年轻男声,有力量感
  • 白桦:成熟男声,沉稳

2. 节拍器 click / 循环音轨

全部托管在腾讯云 COS2026-05-26 上传),本地不再保留:

用途 文件 大小 引擎
单拍 click(基础) click.mp3 3.4 KB InnerAudio
单拍 click(木鱼,推荐) click-wood.mp3 4.6 KB InnerAudio (custom 模式)
循环音轨 慢走 80 BPM loop_80bpm_2.mp3 71 KB BgAudio (preset)
循环音轨 健走 110 BPM loop_110bpm_2.mp3 65 KB BgAudio (preset)
循环音轨 快走 130 BPM loop_130bpm_2.mp3 66 KB BgAudio (preset)

微信 BackgroundAudioManager.src 只接受 https URL,不支持包内资源,所以必须走 CDN。 COS 域名已加入小程序后台 downloadFile 合法域名,跑步视频也走同一域名。

更换音色:去 pixabay.com/sound-effects 找新素材 → 上传到 COS → 在 useMetronomeBg.ts / useMetronome.ts 里替换 URL 即可。

3. 背景音乐

每首 30 秒~2 分钟即可(loop 后听不出接缝)。

推荐来源(免费可商用):

文件大小建议 < 1MBmp3 128kbps 单声道即可)。

在代码里被引用的位置

  • training/hooks/useMetronome.tsDEFAULT_CLICK_SRC (CDN: click.mp3)
  • training/hooks/useMetronomeBg.tsLOOP_PRESETS[*].src (CDN: loop_*.mp3)
  • training/pages/metronome.vueCLICK_WOOD_URL (CDN: click-wood.mp3, custom 模式)
  • training/hooks/useVoiceCoach.tsvoice/numbers/*.mp3voice/prompts/*.mp3 (规划中)
  • training/hooks/useTrainingBgm.tsbgm/*.mp3 (规划中)

如需修改路径,修改对应 hook 文件里的常量即可。

注意事项

  • 微信小程序对单个文件大小有 10MB 上限,本目录所有文件加起来建议控制在 5MB 以内。
  • 小程序整包大小限制(主包 2MB / 总包 20MB),如果资源较大建议放 CDN 而非本地 static。
    • useVoiceCoach.ts 里的 VOICE_BASE 改成 CDN URL 即可。

后台/锁屏播放设计备忘(未来健身模块用)

节拍器目前用 InnerAudioContext + requiredBackgroundModes:["audio"] 已能覆盖 5~10 分钟健走场景。 真要做长时间训练 BGM、锁屏控制条等高级能力,参考下面的 BackgroundAudioManager 方案。

引擎选择对照表

健身模块的音频天然分两类,配两套引擎不冲突:

音频类型 时长 推荐引擎 理由
训练/休息 BGM 30s~2min 循环 BackgroundAudioManager 长流、需要后台/锁屏不停
语音教练("开始/休息/再来" 1~3s 单句 InnerAudioContext 短促,前台用即可
报数(1, 2, 3... 0.5~1s InnerAudioContext + 池子 高频短促
节拍器 click 50~150ms InnerAudioContext + 池子 极短,BgAudio 不接受

关键约束:BgAudio 是全局单例,一次只能播 1 个音频。所以让它专门播 BGM 这类"长流",其他短音用 InnerAudio 配合,互不打架。

BackgroundAudioManager 限制清单(踩坑预警)

  1. 全局单例:整个小程序同一时刻只能播一个,多场景要协调切换
  2. 音频时长 ≥ 1 秒:太短的 click 会被微信判定异常忽略
  3. 必填 metadatatitle / coverImgUrl / singer / epname / webUrl 缺一会报错
  4. 切 src 有延迟:200~500ms 初始化抖动,频繁切换会卡顿
  5. 必须声明 requiredBackgroundModes:["audio"] 才能后台播放
  6. iOS 锁屏豁免:声明后能锁屏继续播,无 5 分钟时长限制(vs InnerAudio 的 5min
  7. 会显示系统控制条:锁屏/通知栏出现带封面+暂停按钮的控制条

推荐架构(健身模块上线时)

training/hooks/
├── useTrainingBgm.ts    → BackgroundAudioManager (后台/锁屏继续放音乐)
├── useVoiceCoach.ts     → InnerAudioContext     (语音指导,前台用)
└── useMetronome.ts      → InnerAudioContext     (节拍器,池子方案,保持现状)

页面 pages.json 声明:

{
    "path": "pages/index",
    "style": {
        "navigationBarTitleText": "练一练",
        "requiredBackgroundModes": ["audio"]
    }
}

锁屏会显示 BGM 控制条,老人能直接在锁屏点暂停。训练页面通过 bgm.onPause() 监听同步暂停训练,体验连贯。

必备资产清单

到时候要准备的文件:

  • BGM 2 首bgm/train-light.mp3(训练)、bgm/rest-meditation.mp3(休息)
    • 每个 30s1min128kbps 单声道,3080KB
    • 推荐来源:pixabay.com/music 搜 "calm" / "meditation" / "lofi"
  • 锁屏封面audio/cover.jpg 200×200
    • 简洁绿色背景 + 训练 emoji 即可

BackgroundAudioManager API 速查

const bgm = uni.getBackgroundAudioManager()

/* 必填 metadata,缺一会报错 */
bgm.title       = '健走训练中'
bgm.coverImgUrl = '/training/static/audio/cover.jpg'
bgm.epname      = '甄养堂'
bgm.singer      = '健走节拍'
bgm.webUrl      = ''   // 必填,空字符串可

/* src 一旦赋值会自动播放 */
bgm.src = '/training/static/audio/bgm/train-light.mp3'

/* 控制 */
bgm.pause()    // 暂停 (锁屏控制条仍在)
bgm.play()     // 继续
bgm.stop()     // 真停 + 隐藏控制条
bgm.seek(30)   // 跳到 30s

/* 事件监听 — 用户从锁屏点暂停时会触发 */
bgm.onPause(() => { /* 同步训练 UI 状态 */ })
bgm.onPlay(()  => {})
bgm.onStop(()  => {})
bgm.onEnded(() => { /* 不 loop 时触发,可手动接下一首 */ })
bgm.onError((err) => { console.error(err) })

切换不同场景音乐的模式

function switchBgm(scene: 'training' | 'resting' | 'none') {
    if (scene === 'none') {
        bgm.stop()
        return
    }
    bgm.title = scene === 'training' ? '健走训练中' : '休息恢复中'
    bgm.src = scene === 'training'
        ? '/training/static/audio/bgm/train-light.mp3'
        : '/training/static/audio/bgm/rest-meditation.mp3'
    /* 注:setSrc 会自动播放,有 200~500ms 切换延迟 */
}

节拍器要不要也升级到 BgAudio?

目前不需要。节拍器升级 BgAudio 的代价:

  • 需要预合成 3 个档位的循环 mp380/110/130 BPM × 2 拍)
  • 必须砍掉右下角微调按钮(预合成 mp3 改不了 BPM)
  • 切档位有 200~500ms 卡顿

如果未来发现"健走 30 分钟以上锁屏会停"才考虑改。当前 5~10 分钟场景 InnerAudio + requiredBackgroundModes 够用。