在此示例中,加载页面时执行gamplay()函数,这会在if语句中触发playPattern()。 playPattern接受一个数组,并在数组中的每个元素上调用setTimeout,该元素执行该元素的playAudio。 PlayAudio将一个类添加到一个元素并播放一个音频文件。
document.addEventListener("DOMContentLoaded", function(event) {
function playPattern(arr){
var patternIDs = {
0: 'blue',
1: 'green',
2: 'red',
3: 'yellow'
}
for (var x = 0; x < arr.length; x++) {
var myTimer = setTimeout(playAudio, 1000*x, patternIDs[arr[x]]);
}
clearTimeout(myTimer);
}//Plays a sequence of audio depending on the array values
function playAudio(id){
switch (id){
case 'blue':
document.getElementById('idBtnBlue').classList.add('btn--blue__highlight');
document.getElementById('sound1').play();
break;
case 'red':
document.getElementById('idBtnRed').classList.add('btn--red__highlight');
document.getElementById('sound2').play();
break;
case 'green':
document.getElementById('idBtnGreen').classList.add('btn--green__highlight');
document.getElementById('sound3').play();
break;
case 'yellow':
document.getElementById('idBtnYellow').classList.add('btn--yellow__highlight') ;
document.getElementById('sound4').play();
break;
}
}//Play audio
function removeHighlight(){
document.getElementById('idBtnBlue').classList.remove('btn--blue__highlight');
document.getElementById('idBtnRed').classList.remove('btn--red__highlight');
document.getElementById('idBtnGreen').classList.remove('btn--green__highlight');
document.getElementById('idBtnYellow').classList.remove('btn--yellow__highlight');
}//Remove Highlight
function gamePlay(){
var simonArr = [];
var userArr = [];
var testArr = [3, 2, 0, 1, 3];
if(strict){
playPattern(testArr);
}
else{
playPattern(testArr);
}
removeHighlight();
}//main gameplay
gamePlay(); //Start game play
});
我想要做的是在播放完阵列中的所有元素后调用removeHighlight()。
我试图在playPattern中的for循环之后调用removeHighlight(),或者在gameplay()中完成if语句之后调用removeHighlight(),但后来意识到在添加高亮显示之前调用了该函数。
谢谢你的任何帮助表示赞赏。 Codepen
答案 0 :(得分:2)
你可以传递额外的参数来标记最后一项,然后在playAudio中调用函数:
var myTimer = setTimeout(playAudio, 1000*x, patternIDs[val],x==arr.length-1);
或者,可能是最简单的方法 - 直接用以下方法调用函数:
setTimeout(removeHighlight, 1000*(arr.length-1));
在playPattern()结束之前。
另外,希望你知道clearTimeout()总是会破坏你的第一个调用,除非在forEach范围内声明变量。
答案 1 :(得分:1)
创建一个promises数组,用于解析音频元素onended
事件。然后使用Promise.all(array).then( /* un highlight function reference*/)
清除所有承诺已解决的亮点。
const patternNames = "blue,green,red,yellow".split(",");
const patternElements = patterNames.map(name => document.getElementById("idBtn" + name[0].toUpperCase() + name.substr(1)) );
function playPattern(arr){
function playAudio(id, time){
return new Promise((resolve) => { // create a promise to play audio at time
setTimeout(() => {
var audioElement;
patternElements[id].classList.add("btn--" + patternNames[id] + "__highlight");
(audioElement = document.getElementById("sound" + (id + 1))).play();
audioElement.onended = resolve;
},time);
});
}
Promise.all(patternNames.map((name, i) => playAudio(i, i * 1000))).then(() => { // when all sounds play then
// clear all highlights
patternElements.forEach((name, i) => patternElements[i].classList.remove("btn--" + patternNames[i] + "__highlight"));
})
}
playPattern([1,2,3,0,2,1]);
答案 2 :(得分:1)
setTimeout
函数是异步的,因此playPattern
函数实际上会在任何playAudio
延迟调用之前返回。
这意味着在任何超时回调被触发之前也将调用removeHighlight
。如果你想在播放所有声音时做某事,你需要一种方法来管理你的异步函数调用,并在完成这些调用后发出回调。
我喜欢使用精彩的async库来做这种类型的东西,但你也可以用Promises实现类似的东西。有了它,您可以按如下方式重写playPattern
函数:
function playPattern (arr, done) {
var patternIDs = {
0: 'blue',
1: 'green',
2: 'red',
3: 'yellow'
}
var iteratee = function (item, i, cb) {
setTimeout(function (id) {
playAudio(id)
cb()
}, 1000 * i, patternIDs[arr[i]])
}
async.eachOf(arr, iteratee, done)
}
现在,当您调用playPattern
时,您可以传入一个函数作为第二个参数,只有在所有setTimeout
延迟调用完成后才会调用该函数。您可以按如下方式重写gamePlay
函数:
function gamePlay () {
var testArr = [3, 2, 0, 1, 3]
playPattern(testArray, removeHighlight)
}