跨浏览器准确统计audio标签加载进度,解决加载完成判定异常问题
解决方案:精准跟踪音频加载完成状态
问题根源
不同浏览器对onCanPlay事件的触发逻辑存在差异:Firefox会在音频基本加载完成后触发,但Chrome、Edge、Opera会在音频仅能播放一小段时就提前触发,导致进度跳至100%时,高音等大文件音频还未加载完毕;而onCanPlayThrough依赖浏览器对网络的预估,可靠性极差,无法作为判断标准。
方案1:基于Promise的精确加载跟踪(推荐)
放弃依赖单个事件,改用Promise统一管理所有音频的加载状态,确保所有音频真正加载完成后再结束流程。
步骤1:修改子iframe中的audio标签
移除onCanPlay属性,统一用JS绑定事件:
<audio id='n0' preload='auto'><source src='Sounds/0.mp3' type='audio/mp3'></audio>
步骤2:重构子iframe的JS逻辑
// 存储所有音频加载的Promise实例 const audioLoadPromises = []; // 获取当前乐器的总音频数量 const totalSamples = parent.window.InstrumentsLastSampleRanks[`<?php echo $_POST['Instrument']; ?>`]; // 页面DOM加载完成后初始化 window.addEventListener('DOMContentLoaded', () => { // 遍历所有audio标签 document.querySelectorAll('audio').forEach(audio => { const loadPromise = new Promise((resolve, reject) => { // 音频第一帧数据加载完成(已具备完整播放条件)时触发 audio.addEventListener('loadeddata', () => { updateLoadingProgress(); resolve(); }); // 处理音频加载错误,避免卡住流程 audio.addEventListener('error', (e) => { console.error(`音频加载失败:${audio.id}`, e); resolve(); }); // 主动触发加载 audio.load(); }); audioLoadPromises.push(loadPromise); }); // 所有音频加载完成后执行收尾操作 Promise.all(audioLoadPromises).then(() => { const previewTag = parent.document.getElementById('LoadingPercentageHolder'); previewTag.innerHTML = '100'; // 隐藏加载层,显示选项菜单 Fade(parent.document.getElementById('LoadingDiv')); Fade(parent.document.getElementById('OptionsMenu'),'In'); }); }); // 实时更新加载进度 function updateLoadingProgress() { const previewTag = parent.document.getElementById('LoadingPercentageHolder'); // 统计已完成加载的音频数量 const loadedCount = audioLoadPromises.filter(p => p.status === 'fulfilled').length; const currentlyLoaded = ((loadedCount / totalSamples) * 100).toFixed(3); previewTag.innerHTML = currentlyLoaded; }
核心优势
loadeddata事件触发时,音频数据已加载到本地,确保点击即可播放;- Promise数组统一管理状态,进度计算精准,不会出现跳变;
- 兼容所有主流浏览器,避免浏览器事件逻辑差异导致的问题。
方案2:修复自动播放方案的作用域与音量问题
如果坚持使用自动播放的思路,可通过以下方式解决原有问题:
步骤1:修改audio标签
直接用muted属性替代手动设置音量,避免作用域混乱:
<audio id='n0' preload='auto' autoplay muted><source src='Sounds/0.mp3' type='audio/mp3'></audio>
步骤2:调整子iframe的JS逻辑
// 全局状态标记 let isLoaded = false; let loadedCount = 0; const totalSamples = parent.window.InstrumentsLastSampleRanks[`<?php echo $_POST['Instrument']; ?>`]; window.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('audio').forEach(audio => { // 音频播放完毕(确认完全加载)时触发 audio.addEventListener('ended', () => { loadedCount++; updateLoadingProgress(); // 取消静音,恢复正常音量 audio.muted = false; // 所有音频加载完成 if (loadedCount >= totalSamples) { isLoaded = true; const previewTag = parent.document.getElementById('LoadingPercentageHolder'); previewTag.innerHTML = '100'; Fade(parent.document.getElementById('LoadingDiv')); Fade(parent.document.getElementById('OptionsMenu'),'In'); } }); // 处理播放错误,避免卡住流程 audio.addEventListener('error', () => { loadedCount++; updateLoadingProgress(); }); }); }); // 更新加载进度 function updateLoadingProgress() { const previewTag = parent.document.getElementById('LoadingPercentageHolder'); const currentlyLoaded = ((loadedCount / totalSamples) * 100).toFixed(3); previewTag.innerHTML = currentlyLoaded > 100 ? '100' : currentlyLoaded; } // 后续播放音频的方法 function playAudio(audioId) { const audio = document.getElementById(audioId); if (isLoaded) { audio.play(); } }
核心修复点
- 用
muted属性替代手动设置音量,彻底解决作用域导致的音量无法恢复问题; - 全局变量
loadedCount跟踪加载数量,避免作用域混乱; - 监听
ended事件确认音频已完全加载并播放完毕,确保音频可用。
内容的提问来源于stack exchange,提问作者Phrank
相关产品推荐
相关产品推荐

