
本文详解如何使用纯 javascript 实现多层级音频循环播放:每个音频文件按指定次数重复播放,全部播完后整个播放列表再整体重复若干次,且严格串行、无重叠、可暂停/继续。
本文详解如何使用纯 javascript 实现多层级音频循环播放:每个音频文件按指定次数重复播放,全部播完后整个播放列表再整体重复若干次,且严格串行、无重叠、可暂停/继续。
在构建语音学习、听力训练或背景音效系统时,常需对一组
以下为经过验证、结构清晰、鲁棒性强的解决方案:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
✅ 核心设计原则
- 状态集中管理:使用 isListPlaying 控制全局播放态,audioIndex 指向当前音频,filePlayCount 和 listPlayCount 分别追踪文件级与列表级播放进度。
- 单点定时调度:所有延迟操作统一通过 setTimeout + timeoutId 管理,暂停时调用 clearTimeout(timeoutId) 彻底中断后续调度。
- 事件解耦监听:ended 事件统一委托至 onEnded() 处理函数,避免为每个音频重复绑定/移除监听器。
- 原子化播放动作:playAudioElement() 仅负责重置时间、设置速率并播放,不包含逻辑判断,确保职责单一。
✅ 完整可运行代码
<button id="lsm-play-list" type="button">Play list</button> <audio class="audio-element"><source src="/wp-content/uploads/2023/10/wei-geet-et-iech.mp3" type="audio/mpeg"> Your browser does not support the audio element. </source></audio><audio class="audio-element"><source src="/wp-content/uploads/2023/10/wei-geet-et.mp3" type="audio/mpeg"> Your browser does not support the audio element. </source></audio>
document.addEventListener('DOMContentLoaded', function () {
const playButton = document.getElementById('lsm-play-list');
const audioElems = document.querySelectorAll('.audio-element');
// 配置参数(可动态修改)
const playbackSpeed = 1.5; // 播放速率
const fileRepeatCount = 2; // 单个音频重复次数(含首次)
const listRepeatCount = 2; // 整个列表重复次数(含首次)
const pauseDurationSec = 1; // 音频间静默间隔(秒)
// 内部状态
let audioIndex = 0;
let filePlayCount = 0;
let listPlayCount = 0;
let isListPlaying = false;
let timeoutId = null;
playButton.addEventListener('click', function () {
if (!isListPlaying) {
isListPlaying = true;
playButton.textContent = 'Pause list';
playAudioElement();
} else {
isListPlaying = false;
playButton.textContent = 'Continue';
pauseAudioElement();
}
});
function playAudioElement() {
const audio = audioElems[audioIndex];
audio.currentTime = 0;
audio.playbackRate = playbackSpeed;
audio.play().catch(e => console.warn('Audio play failed:', e));
}
function pauseAudioElement() {
audioElems[audioIndex].pause();
clearTimeout(timeoutId);
}
// 统一处理所有音频的 ended 事件
audioElems.forEach(audio => audio.addEventListener('ended', onEnded));
function onEnded() {
filePlayCount++;
// 判断是否为当前列表轮次的最后一项且已达到单文件播放次数
const isLastAudioInList = (audioIndex === audioElems.length - 1);
const isFileRepeatDone = (filePlayCount === fileRepeatCount);
if (isLastAudioInList && isFileRepeatDone) {
listPlayCount++;
if (listPlayCount {
if (isListPlaying) {
playAudioElement();
}
}, pauseDurationSec * 1000);
}
});
⚠️ 关键注意事项
- 错误捕获:audio.play() 可能因浏览器策略(如自动播放限制)抛出 Promise Rejection,建议添加 .catch() 处理,避免阻塞流程。
- 暂停健壮性:pauseAudioElement() 必须调用 clearTimeout(timeoutId),否则即使暂停,延迟后的 playAudioElement() 仍会执行,造成状态错乱。
- 配置灵活性:playbackSpeed、fileRepeatCount 等参数可封装为函数入参或从 DOM 属性读取,便于运行时动态调整。
- 无障碍支持:建议为按钮添加 aria-label,为
该方案彻底规避了原始代码中因递归调用 playAudio(index) 导致的监听器堆积与状态不同步问题,以声明式状态驱动 + 单点定时器为核心,实现了高可靠性、易维护的多级音频循环播放逻辑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










