“我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!”
周五摸鱼,摸了个羊了个羊的简易版本,UI 其实很简单(搞这玩意儿有点像刷 leetcode)
码上掘金地址
思路
- 随机生成一些数据,给卡片显示用
- 初始化一堆卡片,并定义它们的基本数据(内容、样式、偏移量、层级 z-index等),v-for 显示在 div
- 动态随机删除一些数据(因为卡片是根据 translate 移动的,所以移除某些,不影响其他卡片)
界面
一开始想用纯 html 写,但是好久不写了,操作 UI 太费劲了,就搞了个 Vue 的版本
- css 中就几个样式(白色卡片样式、灰色卡片样式)
- 绝对布局,通过每个卡片的 translate 来控制偏移
- 用的 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. 计算哪些卡片是被遮挡的
初始化所有卡片的内容、位移后,如何判断哪些是被遮挡的?
其实就是判断两个矩形是否相交,并根据所在层级level,判断谁在顶上,谁在底下
所以同时满足两个条件,即可认为存在遮挡。
- 两个矩形中心点的X轴的 距离A,是否小于各自宽度一半的和
- 两个矩形中心点的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,羊了个羊,本来就赢不了
代码路径
目前支持两种卡片生成方式:
- 随机生成卡片
- 读取 json 数据,生成卡片