翻卡牌小游戏
游戏规则比较简单,没有时间限制,通过鼠标点击翻开卡牌。一次点击翻开一张卡牌,两张卡牌图案一致便可以抵消,不一致卡牌自动翻转。全部卡牌抵消完后,游戏结束。(下面有完整代码)
1 编写样式
/* 设置游戏背景大小 */
body {
width: 600px;
height: 700px;
text-align: center;
margin: 0 auto;
background-color: chocolate;
}
/* 简述游戏和规则的盒子大小 */
#ps{
height: 100px;
width: 600px;
}
/* 卡槽的大小,用于存放卡牌 */
.cardBox{
width: 75px;
height: 75px;
float: left;
}
/* 设置每张卡牌的大小 */
img {
width: 71px;
height: 71px;
margin-right: 2px;
margin-left: 2px;
transition: transform 1s;
display: block;
}
/* 给选中的卡牌一个特殊的样式,例如外框+1显示为蓝色 */
.selectedPic {
border: 1px solid rgb(81, 0, 255);
}
2 再编写一个div用于介绍游戏
<div id="ps">
<h1>翻卡牌</h1>
<p>翻中相同卡牌便会抵消,卡牌全部抵消,游戏结束</p>
</div>
3 编写js代码
创建两个对象block1和block2,用于表示两张被翻开的卡牌对象,再声明两个变量sumCar和counter,分别用于表示当前所剩的卡牌数量以及,以及点击数(后面解释它的作用)。
// 临时卡牌对象1
var block1;
// 临时卡牌对象2
var block2;
// 现有卡牌数量
var sumCar = 64;
// 记录当前点击数
var counter = 0;
创建一个二维数组board,遍历并赋值,里面存储值小于16,因为卡牌的图案最多只有16张。这里可以根据自己的图案数量来决定borad数组存储的值的大小。这里使用j += 2和board[i][j] = board[i][j]是为了保证图案是偶数,防止最后卡牌不能全部抵消。
// 创建一个卡牌数组
var board = new Array();
// 循环并给卡牌赋值,此处赋值最大值为16,因为后续引用的卡牌图案索引最大为16
for(var i = 0; i < 8; i++) {
board[i] = new Array();
for(var j = 0; j < 8; j += 2) {
board[i][j] = board[i][j + 1] = Math.floor(Math.random() * 16) + 1;
}
}
经过上面的遍历赋值后就会得到成对排列值的数组对象了,我们要把数组里面的值打乱。
//循环打乱卡牌
for(var i = 0; i < board.length * board.length * 100; i++) {
var x1 = Math.floor(Math.random() * board.length);
var y1 = Math.floor(Math.random() * board[0].length);
var x2 = Math.floor(Math.random() * board.length);
var y2 = Math.floor(Math.random() * board[0].length);
//交换卡牌位置
var temp = board[x1][y1];
board[x1][y1] = board[x2][y2];
board[x2][y2] = temp;
}
将二维数组里的值打乱之后,我们要为每一个值(卡牌)添加一个相同的点击事件,同时给它们一个相同的背面图案(0.jpeg)。给每一个img命名pic+i+j(这样命名方便,并且每一个img的id都不一样,可以区分它们),这里将它们都分别放在对应的div盒子里样式为cardBox。div相当于一个卡槽,便于将每一张卡牌分隔开来,同时也为防止后面卡牌旋转时造成卡牌位置偏移。使用document.write()将卡牌打印的网页中。
//给每个卡牌都绑定一个点击事件
for(var i = 0; i < 8; i++) {
for(var j = 0; j < 8; j++) {
var p = board[i][j];
document.write("<div class='cardBox'><img src='img/" + 0 + ".jpeg' onclick='select(" + i + "," + j + ")' id='pic" + i + j + "'/></div>");
}
document.write("</br>");
}
执行到这一步时,在页面可以看到如下效果:
接下处理点击事件,也就上面为每一张卡牌绑定的点击事件。上面说的counter的作用就在这里体现了。可以作为判定条件。每次点击时使用样式document.getElementById("pic" + i + j).style.transform = 'rotateY(' + 180 + 'deg)'并配合前面样式里的transition: transform 1s(可以实现一个翻牌的效果)。当我们第一次点击卡牌时,会将二维数组的i和j的值传进去。利用这两个值可以二维数组的对应的预先存储的值(也就是一开始存储的小于16的值)。利用这个值给我们的卡牌对象赋值图案(document.getElementById("pic" + i + j).src = 'img/' + board[i][j] + '.jpeg')。同时将第一次修改后的卡牌对象赋值给全局对象block1。用于后续比较。第二次点击逻辑一致,只是第二次的卡牌对象赋值给block2。
// 事件函数
function select(i, j) {
// 判断counter是否小于2,小于2,执行后续操作。大于等于2说明此时卡牌已经翻开两张。等待它们比较完后,才能继续翻牌。
if(counter < 2){
// 用于表示翻开的牌数。0代表0张,1代表1张,2代表2张。
counter++;
// 第一次点击时
if(counter == 1) {
document.getElementById("pic" + i + j).style.transform = 'rotateY(' + 180 + 'deg)';
var a = new Promise(()=>{
setTimeout(()=>{
document.getElementById("pic" + i + j).className = 'selectedPic';
document.getElementById("pic" + i + j).src = 'img/' + board[i][j] + '.jpeg';
block1 = document.getElementById("pic" + i + j);
},200);
});
}
// 第二次点击时
if(counter == 2) {
document.getElementById("pic" + i + j).style.transform = 'rotateY(' + 180 + 'deg)';
var a = new Promise(()=>{
setTimeout(()=>{
document.getElementById("pic" + i + j).className = 'selectedPic';
document.getElementById("pic" + i + j).src = 'img/' + board[i][j] + '.jpeg';
block2 = document.getElementById("pic" + i + j);
promiseResolve("翻完卡牌了");
},200);
});
}
// counter==2表示已经有两张卡牌是翻开的,可以执行比较
if(counter == 2) {
execute();
}
console.log(counter);
}else{
alert("点击太快了,骚年");
}
}
block1和block2被赋值后表示玩家点击了两次卡牌。可以进行比较。先执行excute()函数。这里使用了await与async是为了等待翻牌的动作结束后再进行比较,不然会看不到第二次翻牌的动画。因为前面使用的图案的转换是在异步中进行的,所以主线程会直接继续执行。在第二次点击时,block2可能还没有来得及赋值,两张卡牌的比较就结束了。所以这里使用async+await的方式进行等待。等block2执行完(也就是上面的promiseResolve("翻完卡牌了"))。这里才继续往下执行。
// 定义一个函数,用于等待第二张卡牌翻开后,再比较能否抵消
function awaitTurnCard(){
return new Promise(resolve => promiseResolve = resolve);
}
// 定义一个函数,用于执行并判断两张卡牌是否能抵消
async function execute(){
var a = await awaitTurnCard();
if(canRemove(block1, block2)) {
setTimeout('removeBlocks(block1, block2)', 1500);
} else {
setTimeout('handler()', 1500); //1500毫秒
}
}
在判断条件里首先判断两张卡牌是否抵消。两种可能不能抵消,1是两次点击的同一张卡牌,二是两张卡牌图案不一样。能抵消则返回ture。
// 定义一个函数,用于判断两张卡牌是否可以消除
function canRemove(block1, block2) {
//判断两次点击是否为同一张牌
if(block1 === block2) {
return false;
}
// 判断两张卡牌的src是否相同
if(block1.src !== block2.src) {
return false;
}
return true;
}
// 定义一个函数,用于消除两张卡牌
function removeBlocks(block1, block2) {
block1.style.transform = 'none';
block2.style.transform = 'none';
block1.style.display = 'none';
block2.style.display = 'none';
block1.className = '';
block2.className = '';
block1 = null;
block2 = null;
counter = 0;
sumCar = sumCar-2;
if(sumCar == 0){
alert("恭喜你,通关了!!!");
}
}
能抵消则调用上面的方法。不能抵消则调用下面方法将卡牌还原。
// 定义一个函数,用于处理抵消失败后还原卡牌
function handler() {
block1.style.transform = 'none';
block2.style.transform = 'none';
setTimeout(()=>{
block1.className = '';
block2.className = '';
block1.src = 'img/' + 0 + '.jpeg';
block2.src = 'img/' + 0 + '.jpeg';
block1 = null;
block2 = null;
counter = 0;
},200);
}
至此,小项目介绍完毕。
完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
/* 设置游戏背景大小 */
body {
width: 600px;
height: 700px;
text-align: center;
margin: 0 auto;
background-color: chocolate;
}
/* 简述游戏和规则的盒子大小 */
#ps{
height: 100px;
width: 600px;
}
/* 卡槽的大小,用于存放卡牌 */
.cardBox{
width: 75px;
height: 75px;
float: left;
}
/* 设置每张卡牌的大小 */
img {
width: 71px;
height: 71px;
margin-right: 2px;
margin-left: 2px;
transition: transform 1s;
display: block;
}
/* 给选中的卡牌一个特殊的样式,例如外框+1显示为蓝色 */
.selectedPic {
border: 1px solid rgb(81, 0, 255);
}
</style>
</head>
<body>
<div id="ps">
<h1>翻卡牌</h1>
<p>翻中相同卡牌便会抵消,卡牌全部抵消,游戏结束</p>
</div>
<script>
// 临时卡牌对象1
var block1;
// 临时卡牌对象2
var block2;
// 现有卡牌数量
var sumCar = 64;
// 记录当前点击数
var counter = 0;
// 创建一个卡牌数组
var board = new Array();
// 循环并给卡牌赋值,此处赋值最大值为16,因为后续引用的卡牌图案索引最大为16
for(var i = 0; i < 8; i++) {
board[i] = new Array();
for(var j = 0; j < 8; j += 2) {
board[i][j] = board[i][j + 1] = Math.floor(Math.random() * 16) + 1;
}
}
//console.log(board);
//循环打乱卡牌
for(var i = 0; i < board.length * board.length * 100; i++) {
var x1 = Math.floor(Math.random() * board.length);
var y1 = Math.floor(Math.random() * board[0].length);
var x2 = Math.floor(Math.random() * board.length);
var y2 = Math.floor(Math.random() * board[0].length);
//交换卡牌位置
var temp = board[x1][y1];
board[x1][y1] = board[x2][y2];
board[x2][y2] = temp;
}
//console.log(board);
//给每个卡牌都绑定一个点击事件
for(var i = 0; i < 8; i++) {
for(var j = 0; j < 8; j++) {
var p = board[i][j];
document.write("<div class='cardBox'><img src='img/" + 0 + ".jpeg' onclick='select(" + i + "," + j + ")' id='pic" + i + j + "'/></div>");
}
document.write("</br>");
}
// 定义一个函数,用于判断两张卡牌是否可以消除
function canRemove(block1, block2) {
//判断两次点击是否为同一张牌
if(block1 === block2) {
return false;
}
// 判断两张卡牌的src是否相同
if(block1.src !== block2.src) {
return false;
}
return true;
}
// 定义一个函数,用于消除两张卡牌
function removeBlocks(block1, block2) {
block1.style.transform = 'none';
block2.style.transform = 'none';
block1.style.display = 'none';
block2.style.display = 'none';
block1.className = '';
block2.className = '';
block1 = null;
block2 = null;
counter = 0;
sumCar = sumCar-2;
if(sumCar == 0){
alert("恭喜你,通关了!!!");
}
}
// 定义一个函数,用于处理抵消失败后还原卡牌
function handler() {
block1.style.transform = 'none';
block2.style.transform = 'none';
setTimeout(()=>{
block1.className = '';
block2.className = '';
block1.src = 'img/' + 0 + '.jpeg';
block2.src = 'img/' + 0 + '.jpeg';
block1 = null;
block2 = null;
counter = 0;
},200);
}
// 定义一个函数,用于等待第二张卡牌翻开后,再比较能否抵消
function awaitTurnCard(){
return new Promise(resolve => promiseResolve = resolve);
}
// 定义一个函数,用于执行并判断两张卡牌是否能抵消
async function execute(){
var a = await awaitTurnCard();
if(canRemove(block1, block2)) {
setTimeout('removeBlocks(block1, block2)', 1500);
} else {
setTimeout('handler()', 1500); //1500毫秒
}
}
// 事件函数
function select(i, j) {
// 判断counter是否小于2,小于2,执行后续操作。大于等于2说明此时卡牌已经翻开两张。等待它们比较完后,才能继续翻牌。
if(counter < 2){
// 用于表示翻开的牌数。0代表0张,1代表1张,2代表2张。
counter++;
// 第一次点击时
if(counter == 1) {
document.getElementById("pic" + i + j).style.transform = 'rotateY(' + 180 + 'deg)';
var a = new Promise(()=>{
setTimeout(()=>{
document.getElementById("pic" + i + j).className = 'selectedPic';
document.getElementById("pic" + i + j).src = 'img/' + board[i][j] + '.jpeg';
block1 = document.getElementById("pic" + i + j);
},200);
});
}
// 第二次点击时
if(counter == 2) {
document.getElementById("pic" + i + j).style.transform = 'rotateY(' + 180 + 'deg)';
var a = new Promise(()=>{
setTimeout(()=>{
document.getElementById("pic" + i + j).className = 'selectedPic';
document.getElementById("pic" + i + j).src = 'img/' + board[i][j] + '.jpeg';
block2 = document.getElementById("pic" + i + j);
promiseResolve("翻完卡牌了");
},200);
});
}
// counter==2表示已经有两张卡牌是翻开的,可以执行比较
if(counter == 2) {
execute();
}
console.log(counter);
}else{
alert("点击太快了,骚年");
}
}
</script>
</body>
</html>
如果想提升一下难度,可以试着进行前后端分离,或是加上时间条,通过抵消卡牌成功来增加时间,失败后减少时间,再加上一些积分排行或是动静分离等等。