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

快速点击卡片导致记忆游戏异常的问题修复求助

快速点击卡片导致记忆游戏异常的问题修复求助

我做了一个记忆配对小游戏,正常慢慢点击卡片的时候,游戏逻辑都能正常运行——翻开第一张,再翻第二张,匹配就保留翻开状态,不匹配就过一会儿合上。但如果我快速狂点卡片,就会出现奇怪的bug:有些明明没配对的卡片会被判定为匹配,一直保持翻开状态,完全打乱游戏节奏。

先贴一下我目前的游戏代码:

HTML 代码

<!DOCTYPE html>
<html>
<body style="justify-self:center;">
  <h1 style="justify-self:center">Memory Game</h1>
  <div id="cont" style="width:px">
    <p class="stats" style="justify-self:center ;"></p>
    <div class='cardContainer' style="justify-self:center ;">
      <div class='card' id="1"></div>
      <div class='card' id="1"></div>
      <div class='card' id="2"></div>
      <div class='card' id="2"></div>
      <div class='card' id="3"></div>
      <div class='card' id="3"></div>
      <div class='card' id="4"></div>
      <div class='card' id="4"></div>
      <div class='card' id="5"></div>
      <div class='card' id="5"></div>
      <div class='card' id="6"></div>
      <div class='card' id="6"></div>
      <div class='card' id="7"></div>
      <div class='card' id="7"></div>
      <div class='card' id="8"></div>
      <div class='card' id="8"></div>
    </div>
    <div style="justify-self:center ;"><button id="reset" style="margin-top: 20px; ">Reset Game</button></div>
    <script src="mg.js"></script>
  </div>
</body>
</html>

JavaScript 代码

const cards = document.querySelectorAll('.card')
let clickCount = 0
let id = []
let matched = 0

function shuffle() {
  cards.forEach(card => {
    let pos = Math.floor(Math.random() * 12);
    card.style.order = pos
  })
}

shuffle()

function checkMatch() {
  if (id[0] == id[1]) {
    return true
  } else {
    return false
  }
}

cards.forEach(card => {
  card.addEventListener('click', () => {
    card.innerHTML = card.id
    id.push(card.id)
    card.setAttribute('open', 'true')
    clickCount += 1
    console.log(clickCount)
    if (clickCount == 2) {
      let result = checkMatch()
      clickCount = 0
      if (result == false) {
        id = []
        setTimeout(() => {
          document.querySelectorAll('[open="true"]').forEach((card) => {
            card.innerHTML = '';
            card.removeAttribute('open', 'true')
          })
        }, 100)
      } else {
        console.log('matched')
        id = []
        document.querySelectorAll('[open="true"]').forEach((card) => {
          card.removeAttribute('open', 'true');
          card.replaceWith(card.cloneNode(true))
        })
        matched += 2
      }
    }
    if (matched == 16) {
      console.log('You win')
    }
  })
})

问题原因分析

这个bug的核心是快速点击会绕过我们的点击计数逻辑,异步操作的状态没有被正确锁定:

  • 当点击第一张卡片后clickCount=1,快速点击多张卡片会让clickCount直接跳到2、3、4... 此时id数组会塞入多个卡片ID,标记为open="true"的卡片也不止两张,后续匹配逻辑直接混乱
  • setTimeout是异步操作,在它等待执行的100ms里,用户的快速点击会继续触发事件,导致状态没有被及时重置,最终出现错误的卡片保留

修复后的完整代码

我调整了逻辑,主要加了状态锁定和点击有效性判断,完美解决了快速点击的问题:

const cards = document.querySelectorAll('.card')
let clickCount = 0
let id = []
let matched = 0
// 新增:标记是否正在处理卡片匹配,防止快速点击干扰
let isProcessing = false

function shuffle() {
  cards.forEach(card => {
    let pos = Math.floor(Math.random() * 12);
    card.style.order = pos
  })
}

shuffle()

function checkMatch() {
  return id[0] === id[1]
}

// 封装点击事件绑定,方便克隆卡片后重新绑定
function bindCardClick(card) {
  card.addEventListener('click', () => {
    // 新增判断:正在处理中、卡片已翻开、无ID(已匹配)的情况直接返回
    if (isProcessing || card.hasAttribute('open') || !card.id) {
      return
    }

    card.innerHTML = card.id
    id.push(card.id)
    card.setAttribute('open', 'true')
    clickCount += 1

    if (clickCount === 2) {
      // 锁定状态,禁止新点击
      isProcessing = true
      const result = checkMatch()
      clickCount = 0

      if (!result) {
        id = []
        setTimeout(() => {
          document.querySelectorAll('[open="true"]').forEach((card) => {
            card.innerHTML = '';
            card.removeAttribute('open', 'true')
          })
          // 处理完成解锁
          isProcessing = false
        }, 500) // 把时间调长,用户有足够时间看卡片,体验更好
      } else {
        console.log('matched')
        id = []
        document.querySelectorAll('[open="true"]').forEach((card) => {
          card.removeAttribute('open', 'true');
          // 克隆卡片并重新绑定事件
          const newCard = card.cloneNode(true)
          bindCardClick(newCard)
          card.replaceWith(newCard)
        })
        matched += 2
        // 处理完成解锁
        isProcessing = false
      }
    }

    if (matched === 16) {
      console.log('You win! 🎉')
      // 这里可以加游戏胜利的弹窗或提示
    }
  })
}

// 初始绑定所有卡片点击事件
cards.forEach(card => bindCardClick(card))

// 新增重置按钮逻辑
document.getElementById('reset').addEventListener('click', () => {
  matched = 0
  clickCount = 0
  id = []
  isProcessing = false
  // 重置所有卡片状态
  document.querySelectorAll('.card').forEach(card => {
    card.innerHTML = ''
    card.removeAttribute('open')
  })
  // 重新洗牌
  shuffle()
})

修复关键要点

  1. 状态锁isProcessing:处理两张卡片匹配逻辑时,禁止新的点击触发,确保每次只处理一组卡片
  2. 点击有效性判断:过滤掉正在处理、已翻开、已匹配的卡片点击,避免无效操作
  3. 优化克隆卡片事件:用函数封装点击绑定,确保克隆后的卡片能正常响应点击
  4. 延长延迟时间:把100ms改成500ms,用户体验更友好,也减少快速点击的概率
  5. 补全重置功能:给重置按钮绑定了逻辑,用户可以随时重启游戏

现在再快速点击卡片,游戏逻辑会保持稳定,不会再出现错误匹配的情况啦~

备注:内容来源于stack exchange,提问作者thiyaharajan rajkumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:25:26