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)。
This commit is contained in:
1 parent
a391021041
commit
4662856963
8 files changed
+1209
-240
No files matched your search
Vendored
+51
-51
@@ -1,64 +1,64 @@
|
||||
// 背景音乐。
|
||||
//
|
||||
// 两条交互约定:
|
||||
// 1. 音源与音量是「用户级覆盖」:切换壁纸不得把用户自定义的音源/音量清回预设默认值。
|
||||
// 2. 音源相同的设置是 no-op:否则两档壁纸共用同一首曲子时,一切换就把音乐从头开始放。
|
||||
export default class AudioController {
|
||||
constructor(audioId, options = {}) {
|
||||
this.audio = document.getElementById(audioId);
|
||||
this.audio.src = options.source;
|
||||
this.audio.volume = options.volume;
|
||||
this.audio.loop = true;
|
||||
this.isPlaying = false;
|
||||
this.isChangingSource = false;
|
||||
this.previousSource = null;
|
||||
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;
|
||||
}
|
||||
if (this.isChangingSource) {
|
||||
return;
|
||||
}
|
||||
if (this.audio) {
|
||||
setTimeout(() => {
|
||||
if (this.isPlaying) return;
|
||||
|
||||
this.audio.play().then(
|
||||
() => {
|
||||
this.isPlaying = true;
|
||||
this.audio.play().catch((error) => {
|
||||
this.isPlaying = false;
|
||||
this.changeSource(this.previousSource);
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.isPlaying = false;
|
||||
// 只重试一次:自动播放被拒时重试多少次都没用,重试只是覆盖"音频尚未就绪"
|
||||
if (this.retryTimer === undefined) {
|
||||
this.retryTimer = setTimeout(() => {
|
||||
this.retryTimer = undefined;
|
||||
this.play();
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.audio.pause();
|
||||
this.isPlaying = false;
|
||||
}
|
||||
changeSource(newSource) {
|
||||
if (this.isChangingSource) {
|
||||
return;
|
||||
}
|
||||
if (this.audio) {
|
||||
this.isChangingSource = true;
|
||||
this.pause();
|
||||
this.previousSource = this.audio.src;
|
||||
this.audio.src = newSource;
|
||||
this.isChangingSource = false;
|
||||
this.play();
|
||||
}
|
||||
}
|
||||
changeVolume(newVolume) {
|
||||
const clampedVolume = Math.max(0, Math.min(1, newVolume));
|
||||
this.audio.volume = clampedVolume;
|
||||
this.changeStateByVolume();
|
||||
}
|
||||
changeStateByVolume() {
|
||||
const currentVolume = this.audio.volume;
|
||||
if (currentVolume > 0 && !this.isPlaying) {
|
||||
this.play();
|
||||
} else if (currentVolume <= 0 && this.isPlaying) {
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
changeOptions(newOptions) {
|
||||
const newSource = newOptions.source;
|
||||
const newVolume = newOptions.volume;
|
||||
this.changeSource(newSource);
|
||||
this.changeVolume(newVolume);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user