共计 5430 个字符,预计需要花费 14 分钟才能阅读完成。
前言
最近一段时间,闲暇之余玩了几把斗地主,可惜的是自己没钱买记牌器,脑子又记不住那么多牌,经常翻车,震怒!遂写记牌器,逆天改命!
界面
不用多说,肯定是模仿斗地主的记牌器最实用好看啦。
ps:这个是最终稿的截图,前几版还有一些按钮,为什么去掉了可以看后面。
交互
首先,怎么设计这个记牌器的交互才是关键,因为我们只能手动记牌,其他牌友出了什么牌,我们就从一副牌里减去出掉的牌。
-
想法 1:键盘输入,回车 | 按钮出牌
- 如果是 1 -9,那肯定是很快的,但是 JQK 大小王这个输入起来太麻烦,如果换成其他键盘上连续的字母来代替 JQK 大小王那么又需要记忆的成本了,我 tm 记忆力好还要个? 记牌器。?!
-
想法 2:点击选取,然后回车 | 按钮出牌
- 这个其实是比较符合我们的斗地主的操作逻辑的,但是有一个问题如果我出的是飞机的话,就要点很多下,非常麻烦!不太行。
-
想法 3:点击 + 滑动直接出牌,去除回车或者按钮,右键撤回
- 这个就是最终的交互逻辑了,如果对手只出了 1 张,那么我们只需要点击就行,如果出连队,飞机,那我们只需要滑几下而已。因为去除了出牌按钮,所以容易点错,这里加入了一个右键给某张牌撤回一张的功能。
实现
很容易就可以想出思路:
- 画出界面,13 种牌(4 张 / 种)+ 大小王(2 张,这里并未区分大小王,因为一般我只是怕炸弹而已,大小王缺一张怕锤子,而且缺了一张以后,不管是大王还是小王都是最大的,有区别吗?)
- 添加左键点击事件,以及左键按下滑动事件。
- 阻止默认右键事件,添加右键点击事件。
- 做一些细节的处理,比如说 4 张牌红色警告,0 张牌,牌面变灰色等等。
实现 1:画界面
这里实在太简单,不想说了。你可以手动写十几个 div,也可以用 js 动态添加。
实现 2:左键点击和滑动处理
我们这里应该分成两布,第一步是选,第二步是出牌。所以我们可以用一个 数组 来保存我们已经选择的牌的序号。
-
左键点击
- 直接将这个牌的序号添加进数组
- 然后出牌
-
滑动
- 两种做法,一种是通过坐标定位,判断鼠标移动前后距离包含了哪几个牌来决定哪些牌应该被选取。第二种就是给每个牌添加
mouseenter
,mouseleave
事件,鼠标经过的时候,就会触发,就把出发的牌的序号添加进数组。 - 然后出牌
- 两种做法,一种是通过坐标定位,判断鼠标移动前后距离包含了哪几个牌来决定哪些牌应该被选取。第二种就是给每个牌添加
实现 3:右键增加牌数
这里唯一要注意的是下面这个,首先屏蔽默认的右键事件。
document.oncontextmenu = function (event) {event.preventDefault();
if (event.button == 2) {addCardCount(event.target)
}
};
然后对 event.target
进行处理就好了。
target
和currentTarget
的区别是:前者可能是绑定事件元素下的子元素,后者是绑定事件元素本身。注意使用区别。
实现 4:状态改变
你看之前我们的界面设计,当牌还有 4 个的时候是红色加粗的,说明可能有炸弹;当没有炸弹时是灰色的;当出完牌以后是带透明的淡灰色。
这里我们只需要根据这个牌数来更改状态就好了。关键是我们怎么改这个牌数会更方便呢?
你可能会使用innerHTML,innerText
?
CSS 中的伪元素中的 before
和after
里面有个 content
属性,而这个属性是可以跟元素上的自定义属性绑定的。比如说
.card::after {content: attr(count);
position: absolute;
bottom: 2px;
right: 5px;
color: #999;
font-size: 18px;
}
那么这个 content
的值就会跟随元素的 count 属性。所以我们出牌的操作其实只需要更改这个属性值就好了,然后根据值来做一些相应的类名添加和删除即可。
适用人群
- 没钱买记牌器的
- 记忆力差的
- 开电脑玩的
后记
使用这个记牌器以后,胜率并没有高多少阿,淦!
我发现是运气不行,别人把把 超级加倍
打? 啊,每天登陆就领几千豆,一把归西!
后续可能写个 运气更改器
!
|
|
|
|
|
|
当然不可能啦。淦!
源码
源码中已经添加诸多备注。复制粘贴即可,后续可能会放到博客上提供下载链接,不用复制那么辛苦啦。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> 记牌器 </title>
<style>
#box {
display: flex;
justify-content: center;
align-items: stretch;
user-select: none;
}
.card {
position: relative;
border-radius: 10px;
width: 60px;
margin-top: 20px;
height: 100px;
background-color: #f5f5f5;
margin-right: 10px;
box-shadow: 0 5px 5px 0 #c9c9c9;
font-size: 28px;
padding-left: 10px;
}
.card--empty {
color: #c3c3c3;
opacity: 0.8;
}
.card--chosen {margin-top: 0;}
.card::after {content: attr(count);
position: absolute;
bottom: 2px;
right: 5px;
color: #999;
font-size: 18px;
}
.card--warning::after {
color: red;
font-weight: bold;
}
#btn-box {
text-align: center;
margin-top: 30px;
}
button {
padding: 10px 30px;
border-radius: 60px;
background-color: orangered;
color: #fff;
font-size: 32px;
border: 0;
cursor: pointer;
transition: all ease .3s;
margin-right: 20px;
outline: none;
}
button:hover {
background-color: red;
transform: scale(1.1);
}
.info {
font-size: 14px;
margin-top: 70px;
color: #999;
}
</style>
</head>
<body>
<div id="box"></div>
<div class="info">
<p> 作者: <a href="http://www.leelei.info">leelei</a></p>
<p> 用法: 鼠标【点击】直接出牌,或者【点击滑动】出牌,右键单击某张牌可以增加牌数(用于防止点错)</p>
<p> 适用于: 斗地主新手,并且没有钱买记牌器,并且经常开着电脑的时候顺便手机玩斗地主 </p>
</div>
</body>
<script>
// 常量
const arr = [3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A', 2, '?'];
const nums = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2];
const TYPE_CHOSEN = 'card--chosen';
const TYPE_WARNING = 'card--warning';
const TYPE_EMPTY = 'card--empty';
// 储存已经选择
let chosenArr = [];
// 添加类名
function addClass(e, name) {if (e.className.indexOf(name) == -1) {e.className += name;}
}
// 移除类名
function removeClass(e, name) {e.className = e.className.replace(name, '')
}
// 初始化卡片
function initCards() {let box = document.getElementById('box');
let frag = document.createDocumentFragment();
for (let i = 0; i < 14; i++) {let ele = document.createElement('div');
ele.className = 'card' + TYPE_WARNING;
ele.innerText = arr[i];
ele.setAttribute('count', nums[i]);
ele.setAttribute('index', i);
frag.append(ele)
}
box.append(frag);
}
// 初始化输入
function initSlideInput() {let cards = document.getElementsByClassName('card');
// 鼠标按下时注册
box.addEventListener('mousedown', installSlideInput)
// 鼠标抬起卸载
box.addEventListener('mouseup', uninstallSlideInput)
box.addEventListener('mouseleave', uninstallSlideInput)
// 注册滑动输入
function installSlideInput() {Array.from(cards).forEach((e) => {e.addEventListener('mouseenter', slideSelect)
e.addEventListener('mouseleave', slideSelect)
})
}
// 卸载滑动输入
function uninstallSlideInput() {Array.from(cards).forEach((e) => {e.removeEventListener('mouseenter', slideSelect)
e.removeEventListener('mouseleave', slideSelect)
})
submit();}
// 点击输入, 一直有效
Array.from(cards).forEach((e) => {e.addEventListener('click', function (event) {slideSelect(event);
submit();})
})
// 滑动输入
function slideSelect(event) {let idx = event.currentTarget.getAttribute('index');
if (event.currentTarget.className.indexOf(TYPE_CHOSEN) == -1) {addClass(event.currentTarget,TYPE_CHOSEN)
chosenArr.push(idx);
}
}
}
// 重置全部牌的状态,凸起 -> 对齐
function reset() {chosenArr = [];
let cards = document.getElementsByClassName('card');
Array.from(cards).forEach((v, i) => {removeClass(v,TYPE_CHOSEN)
});
}
// 出牌
function submit() {let cards = document.getElementsByClassName('card');
chosenArr.forEach((v, i) => {cards[v].setAttribute('count', cards[v].getAttribute('count') - 1);
removeClass(cards[v],TYPE_WARNING);
if (cards[v].getAttribute('count') < 1) {
// 最小为 0, 设置为空状态
cards[v].setAttribute('count', 0);
addClass(cards[v],TYPE_EMPTY)
}
});
// 重置状态
reset();}
// 右键加牌,防止出错牌
function addCardCount(e) {if (e.getAttribute('count') != undefined) {e.setAttribute('count', e.getAttribute('count') - 0 + 1);
}
if (e.getAttribute('count') >= nums[e.getAttribute('index')]) {e.setAttribute('count', nums[e.getAttribute('index')]);
addClass(e,TYPE_WARNING)
} else if (e.getAttribute('count') > 0) {removeClass(e,TYPE_EMPTY);
}
}
window.onload = function () {document.oncontextmenu = function (event) {event.preventDefault();
if (event.button == 2) {addCardCount(event.target)
}
};
initCards();
initSlideInput();}
</script>
</html>