You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Vanilla JavaScript开发Simon游戏时音频顺序播放异常求助

Simon游戏音频序列播放问题修复

问题描述

开发Simon游戏初期,已成功生成随机颜色数组,但playSequentially函数无法按预期依次播放对应音频:重复出现的音频(如示例中的red)常无法再次播放,行为不可预测。例如数组['purple', 'red', 'blue', 'red', 'purple', 'red']中,purple可播放两次,但red仅播放一次。尝试过重置currentTime、修改ended属性等方法未解决。

问题代码

JavaScript代码

let greenBtn = document.getElementById("green-button");
let redBtn = document.getElementById("red-button");
let blueBtn = document.getElementById("blue-button");
let purpleBtn = document.getElementById("purple-button");
let greenAudio = document.getElementById("green-audio");
let redAudio = document.getElementById("red-audio");
let blueAudio = document.getElementById("blue-audio");
let purpleAudio = document.getElementById("purple-audio");
let playback = document.getElementById("play");
greenBtn.addEventListener("click", () => {
  greenAudio.play();
})
redBtn.addEventListener("click", () => {
  redAudio.play();
})
blueBtn.addEventListener("click", () => {
  blueAudio.play();
})
purpleBtn.addEventListener("click", () => {
  purpleAudio.play();
})
let audioArray = [greenAudio, blueAudio, redAudio, purpleAudio];
function playAudio(audio) {
  audio.play();
}
function getAudioFromColor(color) {
  switch (color) {
    case "green":
      return greenAudio;
    case "blue":
      return blueAudio;
    case "red":
      return redAudio;
    case "purple":
      return purpleAudio;
  }
}
//create random array of colors of length n
function getRandomColorArray(n) {
  let randomColorArray = []
  let colors = ["green", "blue", "red", "purple"];
  let i = 0;
  while (i < n) {
    let index = Math.floor(Math.random() * 4);
    console.log(index)
    randomColorArray.push(colors[index]);
    i++;
  }
  return randomColorArray;
}
function playSequentially(audioColorArray) {
  let delay = 500;
  let currentAudio = getAudioFromColor(audioColorArray[0]);
  currentAudio.play();
  for (let i = 0; i < audioColorArray.length - 1; i++) {
    delay += 500;
    currentAudio = getAudioFromColor(audioColorArray[i]);
    currentAudio.onended = setTimeout(() => {
      getAudioFromColor(audioColorArray[i + 1]).play();
    }, delay);
  }
}
playback.addEventListener("click", () => {
  // let randomColorArray = getRandomColorArray(5);
  let randomColorArray = ['purple', 'red', 'blue', 'red', 'purple', 'red']
  console.log(randomColorArray)
  playSequentially(randomColorArray);
});

CSS代码

div {
  margin: 10px;
  /* padding: 10px; */
}
.game-btn {
  border: 1px solid black;
  border-radius: 20%;
  width: 200px;
  height: 200px;
}
.green {
  background-color: green;
}
.blue {
  background-color: blue;
}
.red {
  background-color: red
}
.purple {
  background-color: purple;
}
.green:hover {
  background-color: lightgreen;
}
.blue:hover {
  background-color: lightblue;
}
.red:hover {
  background-color: lightcoral
}
.purple:hover {
  background-color: plum;
}
#play {
  width: 100px;
  background-color: pink;
}

HTML代码

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<title>Document</title>
<div class="container" style="max-width: 75%;">
  <div class="row justify-content-center">
    <div class=" btn" id="play"> Playback</div>
  </div>
  <div class="row justify-content-center">
    <div class="game-btn green btn" id="green-button"></div>
    <audio id="green-audio" src="./sounds/539252-Kalimba-INTIMATE-D5-01.wav"></audio>
    <div class="game-btn blue btn" id="blue-button"></div>
    <audio id="blue-audio" src="./sounds/539256-Kalimba-INTIMATE-E6-01.wav"></audio>
  </div>
  <div class="row justify-content-center">
    <div class="game-btn red btn" id="red-button"></div>
    <audio id="red-audio" src="./sounds/539257-Kalimba-INTIMATE-F4-01.wav"></audio>
    <div class="game-btn purple btn" id="purple-button"></div>
    <audio id="purple-audio" src="./sounds/539258-Kalimba-INTIMATE-F5-01.wav"></audio>
  </div>
</div>
<script src="./assignment22.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>

问题根源

playSequentially函数的实现逻辑存在两个核心问题:

  • 重复覆盖onended事件:当同一音频元素(如redAudio)多次出现时,后续循环会覆盖之前设置的onended回调,导致之前的回调无法触发。
  • 错误的延迟逻辑:使用递增的固定延迟与onended事件结合,会导致时序混乱——音频播放时长可能不等于固定的500ms,延迟叠加后会让后续播放时机完全错位。

修复方案

改用递归方式处理序列播放,每次等待当前音频播放结束后再启动下一个,同时确保播放前重置音频状态:

function playSequentially(audioColorArray) {
  // 递归播放函数
  function playNext(index) {
    if (index >= audioColorArray.length) return; // 序列播放完成
    
    const audio = getAudioFromColor(audioColorArray[index]);
    // 重置音频状态:回到起点,暂停之前的播放(如果有的话)
    audio.currentTime = 0;
    audio.pause();
    
    // 播放当前音频,结束后触发下一个
    audio.play().then(() => {
      audio.onended = () => {
        playNext(index + 1);
      };
    }).catch(err => {
      console.error("播放失败:", err);
      playNext(index + 1); // 即使播放失败,继续下一个
    });
  }
  
  playNext(0); // 从第一个元素开始播放
}

额外优化建议

  • 为按钮点击事件也添加音频重置逻辑,避免重复点击时音频无法播放:
greenBtn.addEventListener("click", () => {
  greenAudio.currentTime = 0;
  greenAudio.play();
})
// 其他按钮同理
  • 添加播放状态锁,防止多次点击Playback按钮导致多个序列同时播放:
let isPlaying = false;

playback.addEventListener("click", () => {
  if (isPlaying) return;
  isPlaying = true;
  
  let randomColorArray = ['purple', 'red', 'blue', 'red', 'purple', 'red'];
  console.log(randomColorArray);
  
  playSequentially(randomColorArray, () => {
    isPlaying = false; // 播放完成后解锁
  });
});

// 修改playSequentially函数,接收完成回调
function playSequentially(audioColorArray, onComplete) {
  function playNext(index) {
    if (index >= audioColorArray.length) {
      onComplete?.();
      return;
    }
    
    const audio = getAudioFromColor(audioColorArray[index]);
    audio.currentTime = 0;
    audio.pause();
    
    audio.play().then(() => {
      audio.onended = () => {
        playNext(index + 1);
      };
    }).catch(err => {
      console.error("播放失败:", err);
      playNext(index + 1);
    });
  }
  
  playNext(0);
}

内容的提问来源于stack exchange,提问作者Laura Mansfield

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:00:00