Files
SpineWallpaper/dist/scripts/audio-controller.js
T
Shuery 4662856963 fix: 初始化竞态、用户级覆盖语义与死代码(重构四个控制器)
初始化竞态(已实证):旧 changeConfig() 整个函数体都在 if (this.player) 里,含配置赋值,
WE 在 window.load 之前下发属性时被静默丢弃,导致背景/音乐已切到新壁纸、立绘还是旧的。
改为「状态先写、应用后置」:index.js 用单一 state 记录(presetId/audioSource/audioVolume),
render() 决定能否立刻应用;setConfig() 无条件记录配置,create() 用记下的配置创建。

- 删除 index.js 里重复的 wallpaperRegisterListener(该 API 在本机 WE 不存在)
- 删除 adjustCanvas():播放器每帧自己做 renderer.resize(1) 且已注册 resize 监听,
  而它用 getBoundingClientRect() 写 canvas.width 却不乘 DPR,属于有害的重复实现
- 用户级音源/音量覆盖:切壁纸不再清回预设默认值(旧 PresetController 的启发式已移除)
- 同源设置改为 no-op:否则两档壁纸共用同一首曲子时一切换就从头播放
- 只在壁纸真的变了才重建播放器,否则拖动音量滑块会触发重新下载 3.6MB 骨架+贴图
- 未知 preset id 保持现状并告警,不再静默回退

验证:属性在 DOMContentLoaded 下发时玩家动画正确为 animation(kv37);
用户自定义音源 custom-track.mp3 与音量 0.2 在切壁纸后完整保留;
重发同一预设时音乐从 1.3s 连续到 1.9s 未重启;未知 id 不崩。
对拍:与重构前同强冻结设置 8 批次 6 配置逐像素完全相同(差异 0)。
2026-09-19 19:53:15 +08:00

65 lines
1.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 背景音乐。
//
// 两条交互约定:
// 1. 音源与音量是「用户级覆盖」:切换壁纸不得把用户自定义的音源/音量清回预设默认值。
// 2. 音源相同的设置是 no-op:否则两档壁纸共用同一首曲子时,一切换就把音乐从头开始放。
export default class AudioController {
constructor(audioId, options = {}) {
this.audio = document.getElementById(audioId);
this.isPlaying = false;
this.retryTimer = undefined;
this.audio.loop = true;
if (options.source) this.setSource(options.source);
if (options.volume !== undefined) this.setVolume(options.volume);
}
// audio.src 会被解析成绝对 URL;预设里的 source 也是绝对 URL(preset.js 用 import.meta.url 推导)
get source() {
return this.audio.src || "";
}
setSource(source) {
if (!source) return;
if (this.source === new URL(source, document.baseURI).href) return;
const wasPlaying = this.isPlaying;
this.audio.src = source;
this.audio.load();
if (wasPlaying) this.play();
}
setVolume(volume) {
const value = Math.max(0, Math.min(1, Number(volume)));
this.audio.volume = Number.isFinite(value) ? value : 1;
if (this.audio.volume <= 0) this.pause();
else if (!this.isPlaying) this.play();
}
play() {
if (this.isPlaying) return;
this.audio.play().then(
() => {
this.isPlaying = true;
},
(error) => {
this.isPlaying = false;
// 只重试一次:自动播放被拒时重试多少次都没用,重试只是覆盖"音频尚未就绪"
if (this.retryTimer === undefined) {
this.retryTimer = setTimeout(() => {
this.retryTimer = undefined;
this.play();
}, 500);
}
},
);
}
pause() {
this.audio.pause();
this.isPlaying = false;
}
}