添加音频重新加载功能

This commit is contained in:
kron
2025-12-29 11:53:19 +08:00
parent 919b06bba0
commit e120ec8e7e
3 changed files with 101 additions and 37 deletions

View File

@@ -126,6 +126,8 @@ class AudioManager {
this.readyMap = new Map(); this.readyMap = new Map();
// 新增:首轮失败的音频集合与重试阶段标识 // 新增:首轮失败的音频集合与重试阶段标识
this.failedLoadKeys = new Set(); this.failedLoadKeys = new Set();
// 加载代数,用于 reloadAll 时作废旧的加载循环
this.loadGeneration = 0;
this.initAudios(); this.initAudios();
} }
@@ -141,9 +143,15 @@ class AudioManager {
this.currentLoadingIndex = 0; this.currentLoadingIndex = 0;
this.failedLoadKeys.clear(); this.failedLoadKeys.clear();
// 增加代数,使得旧的加载循环失效
this.loadGeneration = (this.loadGeneration || 0) + 1;
const currentGen = this.loadGeneration;
this.loadingPromise = new Promise((resolve) => { this.loadingPromise = new Promise((resolve) => {
const finalize = () => { const finalize = () => {
if (currentGen !== this.loadGeneration) return;
const runRounds = (round) => { const runRounds = (round) => {
if (currentGen !== this.loadGeneration) return;
// 达到最大轮次或没有失败项,收尾 // 达到最大轮次或没有失败项,收尾
if (this.failedLoadKeys.size === 0 || round > this.maxRetryRounds) { if (this.failedLoadKeys.size === 0 || round > this.maxRetryRounds) {
this.isLoading = false; this.isLoading = false;
@@ -155,30 +163,40 @@ class AudioManager {
this.failedLoadKeys.clear(); this.failedLoadKeys.clear();
debugLog(`开始第 ${round} 轮串行加载,共 ${retryKeys.length}`); debugLog(`开始第 ${round} 轮串行加载,共 ${retryKeys.length}`);
this.loadKeysSequentially(retryKeys, () => { this.loadKeysSequentially(
retryKeys,
() => {
if (currentGen !== this.loadGeneration) return;
// 如仍有失败项,继续下一轮;否则结束 // 如仍有失败项,继续下一轮;否则结束
if (this.failedLoadKeys.size > 0 && round < this.maxRetryRounds) { if (this.failedLoadKeys.size > 0 && round < this.maxRetryRounds) {
setTimeout(() => runRounds(round + 1), this.retryRoundIntervalMs); setTimeout(
() => runRounds(round + 1),
this.retryRoundIntervalMs
);
} else { } else {
this.isLoading = false; this.isLoading = false;
resolve(); resolve();
} }
}); },
currentGen
);
}; };
// 启动第 1 轮重试(如有失败项) // 启动第 1 轮重试(如有失败项)
runRounds(1); runRounds(1);
}; };
this.loadNextAudio(finalize); this.loadNextAudio(finalize, currentGen);
}); });
return this.loadingPromise; return this.loadingPromise;
} }
// 按自定义列表串行加载音频(避免并发过多) // 按自定义列表串行加载音频(避免并发过多)
loadKeysSequentially(keys, onComplete) { loadKeysSequentially(keys, onComplete, gen) {
if (gen !== undefined && gen !== this.loadGeneration) return;
let idx = 0; let idx = 0;
const list = Array.from(keys); const list = Array.from(keys);
const next = () => { const next = () => {
if (gen !== undefined && gen !== this.loadGeneration) return;
if (idx >= list.length) { if (idx >= list.length) {
if (onComplete) onComplete(); if (onComplete) onComplete();
return; return;
@@ -206,7 +224,8 @@ class AudioManager {
} }
// 串行加载下一个音频(首轮) // 串行加载下一个音频(首轮)
loadNextAudio(onComplete) { loadNextAudio(onComplete, gen) {
if (gen !== undefined && gen !== this.loadGeneration) return;
if (this.currentLoadingIndex >= this.audioKeys.length) { if (this.currentLoadingIndex >= this.audioKeys.length) {
debugLog("首轮加载遍历完成", this.currentLoadingIndex); debugLog("首轮加载遍历完成", this.currentLoadingIndex);
if (onComplete) onComplete(); if (onComplete) onComplete();
@@ -220,7 +239,7 @@ class AudioManager {
); );
this.createAudio(key, () => { this.createAudio(key, () => {
setTimeout(() => { setTimeout(() => {
this.loadNextAudio(onComplete); this.loadNextAudio(onComplete, gen);
}, 100); }, 100);
}); });
} }
@@ -530,6 +549,38 @@ class AudioManager {
} }
return Number((loaded / total).toFixed(2)); return Number((loaded / total).toFixed(2));
} }
// 手动重置并重新加载所有音频(用于卡住时恢复)
reloadAll() {
// 1. 停止所有播放
this.stopAll();
// 2. 销毁现有音频实例
for (const audio of this.audioMap.values()) {
try {
audio.destroy();
} catch (_) {}
}
this.audioMap.clear();
// 3. 重置状态
this.readyMap.clear();
this.failedLoadKeys.clear();
this.allowPlayMap.clear();
this.currentPlayingKey = null;
this.sequenceQueue = [];
this.sequenceIndex = 0;
this.isSequenceRunning = false;
// 4. 强制重置加载锁
this.isLoading = false;
this.loadingPromise = null;
this.currentLoadingIndex = 0;
// 5. 重新初始化 (initAudios 会自增 loadGeneration从而终止之前的任何异步循环)
return this.initAudios();
}
} }
// 导出单例 // 导出单例

View File

@@ -66,9 +66,7 @@ const checkAudioProgress = async () => {
if (audioInitProgress.value === 1) return resolve(); if (audioInitProgress.value === 1) return resolve();
audioTimer.value = setInterval(() => { audioTimer.value = setInterval(() => {
const result = AudioManager.getLoadProgress(); const result = AudioManager.getLoadProgress();
if (result > audioProgress.value) {
audioProgress.value = result; audioProgress.value = result;
}
if (audioProgress.value === 1) { if (audioProgress.value === 1) {
setTimeout(() => { setTimeout(() => {
audioInitProgress.value = 1; audioInitProgress.value = 1;
@@ -227,7 +225,12 @@ const goCalibration = async () => {
mode="widthFix" mode="widthFix"
/> />
</view> </view>
<text>资源加载中...</text> </view>
<view>
<text>若加载时间过长</text>
<button hover-class="none" @click="AudioManager.reloadAll">
点击这里重启
</button>
</view> </view>
</view> </view>
</view> </view>
@@ -321,11 +324,21 @@ const goCalibration = async () => {
width: 46rpx; width: 46rpx;
height: 26rpx; height: 26rpx;
} }
.audio-progress > view:nth-child(2) > text { .audio-progress > view:nth-child(3) {
width: 100%; display: flex;
align-items: center;
justify-content: center;
margin-top: 20rpx;
}
.audio-progress > view:nth-child(3) > text {
font-size: 22rpx; font-size: 22rpx;
color: #a2a2a2; color: #a2a2a2;
text-align: center; text-align: center;
margin-top: 10rpx; line-height: 32rpx;
}
.audio-progress > view:nth-child(3) > button {
font-size: 22rpx;
color: #ffe431;
line-height: 32rpx;
} }
</style> </style>

View File

@@ -66,7 +66,7 @@ watch(
:onClick="toUserPage" :onClick="toUserPage"
:size="42" :size="42"
/> />
<view class="user-details" :onClick="toUserPage"> <view class="user-details" @click="toUserPage">
<view class="user-name"> <view class="user-name">
<text>{{ user.nickName }}</text> <text>{{ user.nickName }}</text>
<image <image
@@ -77,7 +77,6 @@ watch(
</view> </view>
<view class="user-stats"> <view class="user-stats">
<text class="level-tag level-tag-first">段位积分</text> <text class="level-tag level-tag-first">段位积分</text>
<!-- <text class="level-tag level-tag-second">LV{{ user.lvl }}</text> -->
<view class="rank-tag"> <view class="rank-tag">
<view <view
class="rank-tag-progress" class="rank-tag-progress"
@@ -173,13 +172,9 @@ watch(
} }
.level-tag-first { .level-tag-first {
width: 50px; padding: 0 10rpx;
background: #5f51ff; background: #5f51ff;
} word-break: keep-all;
.level-tag-second {
width: 60rpx;
background: #09c504;
} }
.level-tag, .level-tag,
@@ -191,14 +186,17 @@ watch(
.rank-tag { .rank-tag {
position: relative; position: relative;
background-color: #00000038; background-color: #00000038;
width: 150rpx; width: 140rpx;
overflow: hidden; overflow: hidden;
word-break: keep-all;
} }
.rank-tag-progress { .rank-tag-progress {
background: #ffa711; background: #ffa711;
height: 100%; height: 100%;
border-radius: 12px; border-radius: 12px;
width: 0;
transition: width 0.3s ease;
} }
.rank-tag-text { .rank-tag-text {
@@ -211,24 +209,26 @@ watch(
} }
.rank-info { .rank-info {
width: 70px; width: 95px;
text-align: left; height: 50px;
font-size: 12px; font-size: 24rpx;
position: relative; position: relative;
color: #b3b3b3; color: #b3b3b3;
padding-left: 8px; padding-left: 12px;
margin-left: 15rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
} }
.rank-info-image { .rank-info-image {
position: absolute; position: absolute;
top: -6px; top: 0;
left: -9px; left: 0;
width: 90px; width: 95px;
} }
.rank-info > text { .rank-info > text {
text-align: center; text-align: center;
word-break: keep-all;
width: 83px;
} }
.rank-number { .rank-number {
display: block; display: block;