我还是一个JS初学者,试图掌握jQuery,并开始将Simon游戏作为一个项目进行编码。你有4个彩色的空格,应该会亮起来&以随机序列播放声音,从长度为1的序列开始。玩家通过点击方块来重现这个序列。如果成功,随机序列长度将增加1,如果您犯了错误,则必须重新开始。
我正处于简单地创建长度为5的随机序列并直观地显示该序列的阶段。这就是我想到的:
var sequence = [];
$("button").click(startGame);
function startGame() {
for (let index = 0; index < 5; index++) {
sequence.push(nextColour());
touchField(index);
// playSound();
}
}
function nextColour() {
var randomNumber = Math.floor(Math.random() * 4) + 1;
if (randomNumber === 1) {
var nextColour = "red";
} else if (randomNumber === 2) {
var nextColour = "blue";
} else if (randomNumber === 3) {
var nextColour = "green";
} else {
var nextColour = "yellow";
}
return nextColour
};
function touchField(index) {
$("." + sequence[index]).addClass("active");
setTimeout(function() {
$("." + sequence[index]).removeClass("active")
}, 300);
}
.active {
border: 1rem solid purple;
}
.red {
border: 1px solid red;
}
.blue {
border: 1px solid blue;
}
.green {
border: 1px solid green;
}
.yellow {
border: 1px solid yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="red" src="https://via.placeholder.com/50" alt="">
<img class="blue" src="https://via.placeholder.com/50" alt="">
<img class="green" src="https://via.placeholder.com/50" alt="">
<img class="yellow" src="https://via.placeholder.com/50" alt="">
所以问题是,那个阶级"。活动的";几乎同时从所有方块中添加和删除,这使得无法区分任何类型的序列。所以我想我可以用";setTimeout()&";。但这并不妨碍它这么做。我相信,在超时后要计算的表达式真正执行之前,代码只是继续运行,从而添加";。活动的";并且几乎同时超时5次,这导致它们几乎同时被移除。理想情况下,在移除"之间有足够的时间;。活动的";并添加";。活动的";到下一个广场。我还尝试将setTimeout添加到";touchSquare()";函数调用。它也不起作用——我相信出于同样的原因,它只是继续执行。我希望你能看到我的问题。^^
我已经研究了其他问题,但答案似乎忽略了浏览器在到达setTimeout后继续执行代码的事实,例如:
如何在我的javascript 中延迟执行
如果您将其复制粘贴到控制台中,它根本不会做它应该做的事情,因为代码在识别setTimeout后仍在执行。我希望我能让你理解我的问题,这是我第一次在StackOverflow上发布问题。如果我能以任何方式改进我提问的方式,我非常感谢你的建设性批评!
一种方法是将setTimeout
用作异步循环,即回调将使用重复的setTimeout
调用来调度新计时器。但是,当你想在结束后继续其他东西时,你很快就会进入所谓的";回调地狱";。
我建议你加入承诺。JavaScript具有async
和await
语法,以简化promise编程。
因此,首先要做的是创建等效的setTimeout
,但作为一个返回promise的函数,您可以创建await
。
然后,代码的其余部分几乎不需要更改。
然而,我建议:
- 将索引存储在
sequence
数组中,而不是颜色名称 - 在开始时将对图像的引用放在jQuery集合中
- 在动画过程中隐藏"开始"按钮--您不希望用户干扰该动画
- 确保每次调用
startGame
时重置sequence
阵列
以下是它的工作原理。忽略我添加的额外CSS,因为我无法访问您的图像。
const sequence = [];
// Load the elements in an node list
const $images = $(".red, .blue, .green, .yellow");
$("button").click(startGame);
// Helper function
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function startGame() {
$("button").hide(); // Don't allow a click on Start while the sequence is animating
sequence.length = 0; // reset
for (let index = 0; index < 5; index++) {
sequence.push(nextColour());
await touchField(sequence[index]);
}
$("button").show();
console.log(sequence);
// Continue with other logic here...
}
function nextColour() {
return Math.floor(Math.random() * 4);
};
async function touchField(index) {
$images.eq(index).addClass("active");
await delay(300);
$images.eq(index).removeClass("active");
await delay(100);
}
.active {
border: 1rem solid purple;
}
.red { background-color: red; }
.blue { background-color: blue; }
.green { background-color: green; }
.yellow { background-color: yellow; }
img {
display: inline-block;
width: 70px;
height: 50px;
border: 1rem solid white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="red" src="/projects/simon-game/images/red.png" alt="">
<img class="blue" src="/projects/simon-game/images/blue.png" alt="">
<img class="green" src="/projects/simon-game/images/green.png" alt="">
<img class="yellow" src="/projects/simon-game/images/yellow.png" alt="">
<p>
<button>Start</button>
要了解它似乎忽略了setTimeout()
并且事情似乎同时发生的原因,您需要了解stack
和event loop
之间的区别。
并发模型与事件循环
放在堆栈中的事情会立即(或尽快)发生,这就是for
循环正在做的事情。
事件循环中的事情发生在给定的最短时间后(after,不完全打开),这就是setTimeout
正在做的。
换言之,for循环将setTimouts放入事件循环中的时间间隔在毫秒内,因此超时在毫秒内触发。事件循环的工作方式与do this、thenthis、then[/strong>that不同。它就像一个";此事件是否达到或超过了超时时间?如果是,则尽快执行";
为了达到您想要的效果,您需要一些递归,或者当函数调用自己时。这样,下一个TouchField()
直到上一个完成后才会被调用,按照您期望的方式进行间隔
我已经做了一些非初学者的事情来简化生成随机序列(使用Array.from()和一个值数组,而不是一个冗长的if/else块),我还添加了第二个超时,这样在显示字段之间就会有一点延迟(否则,同一个字段连续两次模糊在一起),但希望这能帮助你理解这个概念。
let level = 5, sequence; //increment the level /sequence length as the player progresses
$('button').click(function startGame() {
sequence = Array.from({ length: level },
field => ['red','blue','green','yellow'][Math.floor(Math.random() * 4)]);
TouchField(); //no argument to start recursion / show sequence
});
function TouchField(index = 0) {
if (index <= sequence.length - 1) { //recursion exit condition
// playSound();
$('.' + sequence[index]).addClass('active');
setTimeout(function() {
$('img').removeClass('active');
setTimeout(function() {
TouchField(index + 1); //recursion
}, 500);
}, 500);
}
}
.active {
border: 1rem solid purple !important;
}
.red {
border: 1px solid red;
}
.blue {
border: 1px solid blue;
}
.green {
border: 1px solid green;
}
.yellow {
border: 1px solid yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="red" src="https://via.placeholder.com/50" alt="">
<img class="blue" src="https://via.placeholder.com/50" alt="">
<img class="green" src="https://via.placeholder.com/50" alt="">
<img class="yellow" src="https://via.placeholder.com/50" alt="">
<div>
<button>PLAY</button>
</div>