《数了个数》,仿《羊了个羊》

453 阅读2分钟

“我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

周五摸鱼,摸了个羊了个羊的简易版本,UI 其实很简单(搞这玩意儿有点像刷 leetcode)

码上掘金地址

思路

  1. 随机生成一些数据,给卡片显示用
  2. 初始化一堆卡片,并定义它们的基本数据(内容、样式、偏移量、层级 z-index等),v-for 显示在 div
  3. 动态随机删除一些数据(因为卡片是根据 translate 移动的,所以移除某些,不影响其他卡片)

界面

一开始想用纯 html 写,但是好久不写了,操作 UI 太费劲了,就搞了个 Vue 的版本

  1. css 中就几个样式(白色卡片样式、灰色卡片样式)
  2. 绝对布局,通过每个卡片的 translate 来控制偏移
  3. 用的 z-index 控制不同的图层;

  <!-- 标题 -->
  <span class="title">数了个数</span>
  <!-- 持有的 8个 卡片 -->
  <div class="hold-cards-layout">
      <div v-for="cardInfo in holdCardInfos" class="hold-card">
        {{cardInfo.content}}
      </div>
  </div>
  <!-- 堆叠的主卡片们 -->
  <div id="mainLayout" style="margin-top:20px; margin-left:70px;">
      <div v-for="cardInfo in cardInfos" :class="cardInfo.cardClass" 
           @click="OnCardClick(cardInfo.id)"
           :style="cardInfo.translateStyle">
        {{cardInfo.content}}
      </div>
  </div>

逻辑

1. 计算哪些卡片是被遮挡的

初始化所有卡片的内容、位移后,如何判断哪些是被遮挡的?

image.png

image.png

其实就是判断两个矩形是否相交,并根据所在层级level,判断谁在顶上,谁在底下

所以同时满足两个条件,即可认为存在遮挡。

  1. 两个矩形中心点的X轴的 距离A,是否小于各自宽度一半的和
  2. 两个矩形中心点的Y轴的 距离B,是否小于各自高度一半的和

接下来就是“无脑遍历”,但适当增加判断来减少遍历次数(当然还有其他值得优化的地方)

/**
 * 刷新卡片的样式(白色可点击、灰色不可点击)
 */
function findAndChangeBottom() {
  console.log('cardInfos.value.legnth', cardInfos.value.length)
  // 循环每一个卡片,并找到是否有遮挡它的卡片
  cardInfos.value.forEach(aimCard => {
    const coverCardInfo = cardInfos.value.find(item => isCovered(aimCard, item))
    // 有遮挡,则修改该卡片的样式
    if (coverCardInfo) setCardBottom(aimCard)
    else setCardNormal(aimCard)
  });
}
/**
 * 判断是否被覆盖了
 * @param {*} bottomCard 
 * @param {*} topCard 
 */
function isCovered(bottomCard, topCard) {
  if (bottomCard.level >= topCard.level)
    return false;
  const isXFit = Math.abs(topCard.x - bottomCard.x) < CARD_WIDTH
  const isYFit = Math.abs(topCard.y - bottomCard.y) < CARD_HEIGHT
  return isXFit && isYFit
}

2. 按钮点击

  • 被遮挡的卡片不能点
  • 点击后移除该卡片
  • 将点击的卡片,放入持有卡片数组中
  • 持有卡片中,消除3个相同的
  • 刷新卡片们,把从底层变成顶层的卡片,改变样式(灰卡变白卡)
function OnCardClick(id) {
  if (isGameOver) {
    alert("game over, please F5")
    return;
  }
  ...
  // 底层的不能点击
  if (cardInfos.value[index].isBottom) {
    return;
  }
  // 将卡片加入到手卡中
  holdCardInfos.value.push({ ...cardInfos.value[index] })
  // 移除卡片
  cardInfos.value.splice(index, 1)
  // 处理持有的卡片,消除3个相同的
  dealHoldCards()
  if (isGameOver) return;
  if (isGameSuccess())
    return;
  findAndChangeBottom();
}

TODO

目前卡片生成是随机生成的,还有bug,游戏赢不了。。。

不是bug,羊了个羊,本来就赢不了

代码路径

gitee.com/bigflowerfa…

目前支持两种卡片生成方式:

  1. 随机生成卡片
  2. 读取 json 数据,生成卡片