JavaScript小项目-翻卡牌小游戏(有完整代码)

翻卡牌小游戏

        游戏规则比较简单,没有时间限制,通过鼠标点击翻开卡牌。一次点击翻开一张卡牌,两张卡牌图案一致便可以抵消,不一致卡牌自动翻转。全部卡牌抵消完后,游戏结束。(下面有完整代码)

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>

  

如果想提升一下难度,可以试着进行前后端分离,或是加上时间条,通过抵消卡牌成功来增加时间,失败后减少时间,再加上一些积分排行或是动静分离等等。

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值