我正在学习javascript:

我目前正在尝试制作一个游戏,其中动物应该随机产生一到两秒的时间,然后我们可以单击它们并使它们消失。

我遇到了困难,因为我添加了SetTimeout以等待跳转动画结束,以删除其CSS类以删除动物。

但是,动物只显示一次,然后跳跃功能的执行会很快进行,因此动物不会显示。

我尝试调试程序,首先我们在随机计算的同一div上看到动物:

javascript - SetTimeout执行速度比预期的快-LMLPHP

在下一步中,我们看到动物出现在第二个孔上,并且调试器仍然记录已使用的孔是第五个孔:

javascript - SetTimeout执行速度比预期的快-LMLPHP

下一步是看到div 2被使用时:

javascript - SetTimeout执行速度比预期的快-LMLPHP

再次,调试器仍然说这是当动物从1生成时使用的div 2:

javascript - SetTimeout执行速度比预期的快-LMLPHP

另外,如果我尝试使用调试器执行程序,则动物只会第一次显示,然后它们总是隐藏。另外,在控制台中,我们可以看到大量的console.log,其中包含时间和时间。

javascript - SetTimeout执行速度比预期的快-LMLPHP

这是当前代码:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Whack A Mole!</title>
    <link href='https://fonts.googleapis.com/css?family=Amatic+SC:400,700' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<h1>Whack-a-mole! <span class="score">0</span></h1>
<button onClick="startGame()">Start!</button>

<div class="game">
    <div class="hole hole1">
        <div class="mole"></div>
    </div>
    <div class="hole hole2">
        <div class="mole"></div>
    </div>
    <div class="hole hole3">
        <div class="mole"></div>
    </div>
    <div class="hole hole4">
        <div class="mole"></div>
    </div>
    <div class="hole hole5">
        <div class="mole"></div>
    </div>
    <div class="hole hole6">
        <div class="mole"></div>
    </div>
</div>

<script>
    const holes = document.querySelectorAll('.hole');
    const scoreBoard = document.querySelector('.score');
    const moles = document.querySelectorAll('.mole');

    let lastHole;

    function randomTime(min, max) {
        const time = Math.round(Math.random() * (max - min) + min);
        console.log(time);
    }

    function randomHole(holes) {
        const index = Math.floor(Math.random() * holes.length);
        const hole = holes[index];
        if (hole === lastHole) {
            console.log('We calculate a new hole!');
            return randomHole(holes);
        }
        lastHole = hole;
        return hole;
    }

    function jump() {
        let time = randomTime(1000, 2000);
        let hole = randomHole(holes);
        console.log(time, hole);
        hole.classList.add('up');
        setTimeout(() => {
            hole.classList.remove('up');
            debugger;
            jump();
        }, time);
    }

    jump();

</script>
</body>
</html>


我想这是jump()函数,该函数的行为异常,但是,我们如何生成动物,等待过渡并隐藏它,然后使新的动物出现呢?

style.css

html {
  box-sizing: border-box;
  font-size: 10px;
  background: #ffc600;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  padding: 0;
  margin:0;
  font-family: 'Amatic SC', cursive;
}

h1 {
  text-align: center;
  font-size: 10rem;
  line-height:1;
  margin-bottom: 0;
}

.score {
  background:rgba(255,255,255,0.2);
  padding:0 3rem;
  line-height:1;
  border-radius:1rem;
}

.game {
  width:600px;
  height:400px;
  display:flex;
  flex-wrap:wrap;
  margin:0 auto;
}

.hole {
  flex: 1 0 33.33%;
  overflow: hidden;
  position: relative;
}

.hole:after {
  display: block;
  background: url(dirt.svg) bottom center no-repeat;
  background-size:contain;
  content:'';
  width: 100%;
  height:70px;
  position: absolute;
  z-index: 2;
  bottom:-30px;
}

.mole {
  background:url('mole.svg') bottom center no-repeat;
  background-size:60%;
  position: absolute;
  top: 100%;
  width: 100%;
  height: 100%;
  transition:all 0.4s;
}

.hole.up .mole {
  top:0;
}


编辑:

谢谢@斯科特·马库斯(Scott Marcus)的帮助,我非常感谢。

我已经尝试了下面的代码,我将放置跳转功能:

<script>
    const holes = document.querySelectorAll('.hole');
    const scoreBoard = document.querySelector('.score');
    const moles = document.querySelectorAll('.mole');

    let lastHole;
    let timer = null;

    function randomTime(min, max) {
        const time = Math.round(Math.random() * (max - min) + min);
        console.log(time);
    }

    function randomHole(holes) {
        const index = Math.floor(Math.random() * holes.length);
        const hole = holes[index];
        if (hole === lastHole) {
            console.log('We calculate a new hole!');
            return randomHole(holes);
        }
        lastHole = hole;
        return hole;
    }

    function jump() {
        clearTimeout(timer);
        let time = randomTime(1000, 2000);
        let hole = randomHole(holes);
        console.log(time, hole);
        hole.classList.add('up');
        timer = setTimeout(() => {
            hole.classList.remove('up');
            debugger;
            jump();
        }, time);
    }

    jump();

</script>


我发现,如果我们用鼠标滚轮滚动,则会在Opera中生成动物:

javascript - SetTimeout执行速度比预期的快-LMLPHP

但是,我们还没有在脚本上放置evenetListener。

此外,在Mozilla中它们根本不显示:

javascript - SetTimeout执行速度比预期的快-LMLPHP

即使我们在控制台中看到如何计算和显示randomTime和randomHole。

我们如何使动物产卵?

我认为它们之所以不显示,是因为计时器执行得太快,即使我们保存了最后一个计时器,也没有保存最后一个计时器,然后将其清除。

谢谢您的帮助!!

最佳答案

由于您的jump函数包含setTimeout,并且计时器的回调函数对jump进行递归调用,因此您可能会在第一个计时器运行或完成之前发生对jump的第二次调用。这可能导致多个计时器回调函数“堆叠”在事件队列中并运行,一个接一个。这很可能是您所看到的行为。

您需要确保对jump的其他调用不会启动其他计时器,这可能会导致您所描述的效果。

通过确保一次只能运行一个计时器来实现这一点,可以通过捕获计时器的唯一标识符并确保在启动新计时器之前取消最后一个计时器来实现。

var timer = null; // This will hold the most recent timer's id

function jump() {
    clearTimeout(timer); // Cancel any previous timer

    let time = randomTime(1000, 2000);
    let hole = randomHole(holes);
    console.log(time, hole);
    hole.classList.add('up');

    // Capture a reference to the most recent timer
    timer = setTimeout(() => {
        hole.classList.remove('up');
        debugger;
        jump();
    }, time);
}

09-18 00:23