再来一个小游戏——原生js逐句解释开发简易版别踩白块,附源码

芜湖!没想到上个做扫雷的阅读量这么高(激动)
先捞一下:做个小项目~纯原生JS手把手逐句解释写一个扫雷小游戏(附源码)
快看↑
这次就做一个别踩白块吧哈哈哈哈哈
最终版演示地址:钢琴块

源码在最后!最后!

1.画格子

首先,我们不做那么复杂的,每行四个格子,只有一个黑色块,也没有高级的连按的格子
在这里插入图片描述
然后整体格子往下跑,上面生成新的格子,格子按错或者触底了还没按算输,按对了分数+1
似乎比扫雷简单很多(笑)

<html>
  <head>
    <meta charset="utf-8" />
    <title>别踩白块</title>
  </head>
  <body>
    <div id="main">
      <div id="con">
        <div class="row">
          <div class="cell"></div>
          <div class="cell black"></div>
          <div class="cell"></div>
          <div class="cell"></div>
        </div>
      </div>
    </div>
  </body>
</html>

自然而然,CSS就应该是

			* {
				margin: 0;
				padding: 0;
			}

			#con {
				display: flex;
				flex-direction: column-reverse;
				/*设置成竖轴的翻转轴,这样就能让块自动往下掉*/
				justify-content: center
			}

			.row {
				display: flex;
				width: 99%;
				height: 19vh;
				/*这里的高度并不一定是20%,
				我只是想一页有五个,
				如果改小了记得在init方法里把初始的格子的循环多来几次*/
				max-width: 750px;
				/*设置一个最大值,要不电脑端太宽了丑死了*/
			}

			.cell {
				width: 25%;
				height: 99%;
				border-left: 1px solid #CCC;
			}
			.black{
				background-color: #000
			}

如果还不太熟悉flex布局的小伙伴看这里:
===首先在父元素上添加display: flex;,子元素就会变成父元素的项目,默认按照主轴(默认是横轴)排列,flex-direction属性可以修改主轴,这里flex-direction: column-reverse;就修改成了竖轴的翻转轴,justify-content: center表示元素在主轴怎么排列,这里就是居中啦
更多的可以去菜鸟教程什么的看看,非常好学

目前看起来一切正常,我们先把html里的行结构去掉,换上开始游戏的按钮

2.添加开始游戏按钮

<button id="start" onclick="start()">开始游戏</button>

<!--按钮对应的CSS-->
			#start{
				width: 100%;
				height:100%;
				border:0;
				background-color: #000;
				color: #FFF;
				font-size: 25px;
			}
<!--虽然把按钮占满整个屏幕的做法很草率。。但是就先这样吧-->

然后我们就可以准备JS了

3.开始游戏并用js生成格子

既然我们要频繁的生成一行方块,毫无疑问封装一个生成行的函数非常有用,同时也封装一个生成格子的,会更方便一些

当然每个块块是随机的,所以我们定义一个数组,初始值都是cell,然后随机一个改成cell black

function create_row() {
	var list = ['cell', 'cell', 'cell', 'cell'];
	  var i = Math.floor(Math.random() * 4); 
	  list[i] = 'cell black';
	var newrow = document.createElement('div');
		newrow.className="row"
		newrow.appendChild(create_cell(list[0]))
		newrow.appendChild(create_cell(list[1]))
		newrow.appendChild(create_cell(list[2]))
		newrow.appendChild(create_cell(list[3]))
		document.getElementById("con").appendChild(newrow)
}
function create_cell(classname) {
	var newcell= document.createElement('div')
		newcell.className=`${classname}`
	return newcell
}

实现了这些东西东西之后,我们就能方便的考虑怎么开始这个游戏了
我们可以创建一个start方法,这个方法需要先生成几行格子,并且在div #main中添加时间监听,利用事件委托判断点击的是白块还是黑块:

function start(){
	document.getElementById("start").style.display="none"
	for(var i=0;i<5;i++){
	//如果前面格子高度改了,就对应改这里
		create_row()
	}
	document.getElementById('main').onclick = function (ev) {
	    ev = ev || event;
	    console.log(ev.target.classList);
	    //先打印出class留着备用
	  };
}

4.添加点击事件

到了这里,我们基本的框架都差不多了,我们可以开始完善方块的点击事件,如果点击的是黑块就消除这一行,如果是白块嘛,就GG
我们可以再定义一个函数is_black(),用来判断点击的是什么

function is_black(classList){
	if(classList.value.indexOf("black")!=-1){
		return true
	}
	return false
}

那么我们可以修改一下start

	function start() {
		document.getElementById("start").style.display = "none"
		for (var i = 0; i < 5; i++) {
			create_row()
		}
		document.getElementById('main').onclick = function(ev) {
			ev = ev || event;
			if (is_black(ev.target.classList)) {
				ev.path[2].removeChild(ev.path[1])
				//这里path[1]是当前这一行,path[2]是整个div#con
				//然后从DOM删除这一行
			} else {
					GG();
					//失败了,执行的方法
			};
		};
	}

好!现在很有那个味道了
然后就是,点一个块,他就没了,我们得再生成一个
so easy!我们只用在刚刚的删除DOM后面加一个create_row()就好了
真是阶段性的胜利!

5.添加游戏失败和成功事件

随后GG()这个游戏失败的方法就很好写了:
黑白块点到错误的格子,会有变红变白的动画,咱们也要有!

			@keyframes flash
			{
			    0% {background: red;}
			    50% {background: white;}
				100% {background: red;}
			}
			.error_flash{
				animation: flash 1s;
				animation-iteration-count: infinite;
			}

不熟悉css动画的朋友记得去学一学哦
然后我们只要白块的点击事件给她加一个css就好了:

	function GG(eve) {
		eve.className += " error_flash"
		setTimeout(function() {
			alert("输啦");
			document.getElementById("start").style.display = "";
			document.getElementById("con").innerHTML = ""
		}, 3000)
	}

当然,在前面的调用GG()里,我们要把ev.target作为参数传过来

那怎么实现游戏结束呢?我们定义一个总长度就好了嘛,当黑块总数到这个长度了,也不再继续生成了,当所有黑块都点完了,就算赢

我们来总结一下源码

》》基础版源码

<html>
	<head>
		<style>
			* {
				margin: 0;
				padding: 0;
			}

			#con {
				display: flex;
				flex-direction: column-reverse;
				/*这样就能让块自动往下掉*/
				justify-content: center
			}

			.row {
				display: flex;
				width: 99%;
				height: 19vh;
				/*这里的高度并不一定是20%,
				我只是想一页有五个,
				如果改小了记得在init方法里把初始的格子的循环多来几次*/
				max-width: 750px;
				/*设置一个最大值,要不电脑端太宽了丑死了*/
			}

			.cell {
				width: 25%;
				height: 99%;
				border-left: 1px solid #CCC;
			}

			.black {
				background-color: #000;
				border: 1px solid #CCC;
			}

			#start {
				width: 100%;
				height: 100%;
				border: 0;
				background-color: #000;
				color: #FFF;
				font-size: 25px;
			}

			@keyframes flash {
				0% {
					background: red;
				}

				50% {
					background: white;
				}

				100% {
					background: red;
				}
			}

			.error_flash {
				animation: flash 1s;
				animation-iteration-count: infinite;
			}
		</style>
		<meta charset="utf-8" />
		<title>别踩白块</title>
		<link rel="stylesheet" href="css/index.css" />
	</head>
	<body>
		<button id="start" onclick="start()">开始游戏</button>
		<div id="main">
			<div id="con">

			</div>
		</div>
	</body>
</html>
<script type="text/javascript">
	let allNum=10,index=0
	function create_row() {
		index++
		var list = ['cell', 'cell', 'cell', 'cell'];
		var i = Math.floor(Math.random() * 4);
		if(index <= allNum){
		list[i] = 'cell black';}
		var newrow = document.createElement('div');
		newrow.className = "row"
		newrow.appendChild(create_cell(list[0]))
		newrow.appendChild(create_cell(list[1]))
		newrow.appendChild(create_cell(list[2]))
		newrow.appendChild(create_cell(list[3]))
		document.getElementById("con").appendChild(newrow)
	}

	function create_cell(classname) {
		var newcell = document.createElement('div')
		newcell.className = `${classname}`
		return newcell
	}

	function start() {
		index=0
		document.getElementById("start").style.display = "none"
		for (var i = 0; i < 6; i++) {
			create_row()
		}
		scroll(0,document.body.scrollHeight)
		document.getElementById('main').onclick = function(ev) {
			ev = ev || event;
			if (is_black(ev.target.classList)) {
				create_row()
				ev.path[2].removeChild(ev.path[1])
				if(index>=allNum+6){
					alert("win")
				}
			} else {
				GG(ev.target);
			};
		};
	}

	function is_black(classList) {
		if (classList.value.indexOf("black") != -1) {
			return true
		}
		return false
	}

	function GG(eve) {
		eve.className += " error_flash"
		setTimeout(function() {
			alert("输啦");
			document.getElementById("start").style.display = "";
			document.getElementById("con").innerHTML = ""
		}, 3000)
	}
</script>

到这里,其实主要功能已经结束了

花活1:添加动画

接下来我们来玩一点花活,比如让方块的消失有一点动态效果
我们可以利用css过渡,让方块的高度变成0,然后删除dom
先给*添加transition

			* {
				margin: 0;
				padding: 0;
				transition: all 0.5s; 
			}

start方法变为

function start() {
		index=0
		document.getElementById("start").style.display = "none"
		for (var i = 0; i < 6; i++) {
			create_row()
		}
		scroll(0,document.body.scrollHeight)
		document.getElementById('main').onclick = function(ev) {
			ev = ev || event;
			if (is_black(ev.target.classList)) {
				create_row()
				ev.path[1].style.height="0px"
				//高度变为0
				setTimeout(function(){
					ev.path[2].removeChild(ev.path[1])
				},500)
				//0.5s后删除块,
				if(index>=allNum+6){
					setTimeout(function(){
						alert("win")
					},500)
				}
			} else {
				GG(ev.target);
			};
		};
	}

好!看着真棒
那我们来继续搞一搞花活吧
首先,我们添加一个计时器!
我们添加一个gametime的全局变量,在开始游戏是赋值计时器,游戏结束停止计时,看上去完美极了:

/*对应CSS*/
			.time_area{
				position:fixed;
				left: 50%;
				top:10%;
				font-size: 22px;
			}
//全局添加
let gametime = null,
		time = 0

//start()变为
	function start() {
		index = 0
		gametime = setInterval(() => {
			time++
			document.getElementById("time_area").innerHTML=`${Math.floor(time/60)}"${time - 60*Math.floor(time/60)}`
		}, 50)
		document.getElementById("start").style.display = "none"
		for (var i = 0; i < 6; i++) {
			create_row()
		}
		scroll(0, document.body.scrollHeight)
		document.getElementById('main').onclick = function(ev) {
			ev = ev || event;
			if (is_black(ev.target.classList)) {
				create_row()
				ev.path[1].style.height = "0px"
				setTimeout(function() {
					ev.path[2].removeChild(ev.path[1])
				}, 500)
				if (index >= allNum + 6) {
					setTimeout(function() {
						alert("win");
						clearInterval(gametime)
					}, 500)
				}
			} else {
				GG(ev.target);
			};
		};
	}
//GG()变为
	function GG(eve) {
		clearInterval(gametime)
		eve.className += " error_flash"
		setTimeout(function() {
			alert("输啦");
			document.getElementById("start").style.display = "";
			document.getElementById("con").innerHTML = ""
		}, 2000)
	}

欧吼吼吼吼看着真不错
这是目前为止,实现了所有功能的源码:

》》有动画版源码

<html>
	<head>
		<style>
			* {
				margin: 0;
				padding: 0;
				transition: all 0.5s;
			}

			#con {
				display: flex;
				flex-direction: column-reverse;
				/*这样就能让块自动往下掉*/
				justify-content: center
			}

			.row {
				display: flex;
				width: 99%;
				height: 19vh;
				/*这里的高度并不一定是20%,
				我只是想一页有五个,
				如果改小了记得在init方法里把初始的格子的循环多来几次*/
				max-width: 750px;
				/*设置一个最大值,要不电脑端太宽了丑死了*/
			}

			.cell {
				width: 25%;
				height: 99%;
				border-left: 1px solid #CCC;
			}

			.black {
				background-color: #000;
				border: 1px solid #CCC;
			}

			#start {
				width: 100%;
				height: 100%;
				border: 0;
				background-color: #000;
				color: #FFF;
				font-size: 25px;
			}

			@keyframes flash {
				0% {
					background: red;
				}

				50% {
					background: white;
				}

				100% {
					background: red;
				}
			}

			.error_flash {
				animation: flash 1s;
				animation-iteration-count: infinite;
			}
			.time_area{
				position:fixed;
				left: 50%;
				top:10%;
				font-size: 22px;
			}
		</style>
		<meta charset="utf-8" />
		<title>别踩白块</title>
		<link rel="stylesheet" href="css/index.css" />
	</head>
	<body>
		<button id="start" onclick="start()">开始游戏</button>
		<div id="main">
			<div id="con">

			</div>
		</div>
		<div class="time_area" id="time_area"></div>
	</body>
</html>
<script type="text/javascript">
	let allNum = 10,
		index = 0
	let gametime = null,
		time = 0

	function create_row() {
		index++
		var list = ['cell', 'cell', 'cell', 'cell'];
		var i = Math.floor(Math.random() * 4);
		if (index <= allNum) {
			list[i] = 'cell black';
		}
		var newrow = document.createElement('div');
		newrow.className = "row"
		newrow.appendChild(create_cell(list[0]))
		newrow.appendChild(create_cell(list[1]))
		newrow.appendChild(create_cell(list[2]))
		newrow.appendChild(create_cell(list[3]))
		document.getElementById("con").appendChild(newrow)
	}

	function create_cell(classname) {
		var newcell = document.createElement('div')
		newcell.className = `${classname}`
		return newcell
	}

	function start() {
		index = 0
		gametime = setInterval(() => {
			time++
			document.getElementById("time_area").innerHTML=`${Math.floor(time/60)}"${time - 60*Math.floor(time/60)}`
		}, 50)
		document.getElementById("start").style.display = "none"
		for (var i = 0; i < 6; i++) {
			create_row()
		}
		scroll(0, document.body.scrollHeight)
		document.getElementById('main').onclick = function(ev) {
			ev = ev || event;
			if (is_black(ev.target.classList)) {
				create_row()
				ev.path[1].style.height = "0px"
				setTimeout(function() {
					ev.path[2].removeChild(ev.path[1])
				}, 500)
				if (index >= allNum + 6) {
					setTimeout(function() {
						alert("win");
						clearInterval(gametime)
					}, 500)
				}
			} else {
				GG(ev.target);
			};
		};
	}

	function is_black(classList) {
		if (classList.value.indexOf("black") != -1) {
			return true
		}
		return false
	}

	function GG(eve) {
		clearInterval(gametime)
		eve.className += " error_flash"
		setTimeout(function() {
			alert("输啦");
			document.getElementById("start").style.display = "";
			document.getElementById("con").innerHTML = ""
		}, 2000)
	}
</script>

花活2:添加音乐

我们知道,钢琴块点击是会播放声音的
我们也要!
上一个文章,我们介绍了AudioContext
捞一下:做个小钢琴~利用AudioContext获取振荡器并封装成光遇钢琴的样子

我们可以用利用数组,储存一首歌的旋律的音符的频率
用来生成黑块,点击黑块就播放这个声音
这里直接引用一段上面文章的代码

var son1=new AudioContext();
//创建AudioContext
var osc = son1.createOscillator()
//创建音频振荡器
var g = son1.createGain()
//获得音量控制器Gain
osc.connect(g)
//振荡器连接Gain
 osc.type = 'sine'
 //设置波形
 osc.frequency.value = 440
 //设置频率为440Hz,即中音la
g.connect(son1.destination)
//连接扬声器
g.gain.value = 1
//初始音高为1
osc.start();
//从时间轴的此时此刻当前开始发生
var stoptime = 1
osc.stop(stoptime);

这个可以播放一个一秒长的中音la
我们把他封装成一个函数:

var ctx
var sounds=[130,147,165,175,196,220,246,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047]
	function setContent(){
	    if(!ctx){
	        ctx = new AudioContext()
	    }
	}
	function makeSound(hz){
	    // 获得音频上下文
	    setContent();
	    //得到音频振荡器
	    var osc = ctx.createOscillator();
	    //得到音量控制对象
	    var g = ctx.createGain();
	    // 连接振荡器和音量控制对象
	    osc.connect(g);
	    osc.type = 'sine';
	    osc.frequency.value = hz;
	    var duration = 1;
	    g.connect(ctx.destination);
	    g.gain.value = 0;
	    osc.start();
	    g.gain.linearRampToValueAtTime(0.6,ctx.currentTime + 0.01)
	    osc.stop(ctx.currentTime + duration);
	    g.gain.exponentialRampToValueAtTime(0.01 , ctx.currentTime + duration)
	}

在这里补充一下乐理基础:

  1. 频率为波形的一个最小重复单元的长度,单位为Hz
  2. 第一国际音高为机械波440Hz,对应中音la,高音la为440折半,220,低音la为880(这里的高和低特指的是高八度和低八度),不过纯数字算出来的频率会让人听上去音调偏低
  3. 按照第一国际音高,从低8dao,到高8dao的频率约等于[130,147,165,175,196,220,246,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047](加粗标出了基准点)

这里就给出最终版的源码了

》》能演奏的源码

<html>
	<head>
		<style>
			* {
				margin: 0;
				padding: 0;
				transition: all 0.5s;
			}

			#con {
				display: flex;
				flex-direction: column-reverse;
				/*这样就能让块自动往下掉*/
				justify-content: center
			}

			.row {
				display: flex;
				width: 99%;
				height: 19vh;
				/*这里的高度并不一定是20%,
				我只是想一页有五个,
				如果改小了记得在init方法里把初始的格子的循环多来几次*/
				max-width: 750px;
				/*设置一个最大值,要不电脑端太宽了丑死了*/
			}

			.cell {
				width: 25%;
				height: 99%;
				border-left: 1px solid #CCC;
			}

			.black {
				background-color: #000;
				border: 1px solid #CCC;
			}

			#start {
				width: 100%;
				height: 100%;
				border: 0;
				background-color: #000;
				color: #FFF;
				font-size: 25px;
			}

			@keyframes flash {
				0% {
					background: red;
				}

				50% {
					background: white;
				}

				100% {
					background: red;
				}
			}

			.error_flash {
				animation: flash 1s;
				animation-iteration-count: infinite;
			}
			.time_area{
				position:fixed;
				left: 50%;
				top:10%;
				font-size: 22px;
			}
		</style>
		<meta charset="utf-8" />
		<title>别踩白块</title>
		<link rel="stylesheet" href="css/index.css" />
	</head>
	<body>
		<button id="start" onclick="start()">开始游戏</button>
		<div id="main">
			<div id="con">

			</div>
		</div>
		<div class="time_area" id="time_area"></div>
	</body>
</html>
<script type="text/javascript">
	let song=[0,0,2,0,0,2]
	//↑谱子
	var allNum = song.length,
		index = 0,
		create_index=0
	let gametime = null,
		time = 0
	let ctx
	var sounds=[130,147,165,175,196,220,246,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047]
	function create_row() {
		index++
		var list = ['cell', 'cell', 'cell', 'cell'];
		var i = Math.floor(Math.random() * 4);
		if (index <= allNum) {
			list[i] = 'cell black';
		}
		var newrow = document.createElement('div');
		newrow.className = "row"
		newrow.appendChild(create_cell(list[0]))
		newrow.appendChild(create_cell(list[1]))
		newrow.appendChild(create_cell(list[2]))
		newrow.appendChild(create_cell(list[3]))
		document.getElementById("con").appendChild(newrow)
	}

	function create_cell(classname) {
		var newcell = document.createElement('div')
		newcell.className = `${classname}`
		
		return newcell
	}

	function start() {
		index = 0
		gametime = setInterval(() => {
			time++
			document.getElementById("time_area").innerHTML=`${Math.floor(time/60)}"${time - 60*Math.floor(time/60)}`
		}, 50)
		document.getElementById("start").style.display = "none"
		for (var i = 0; i < 6; i++) {
			create_row()
		}
		scroll(0, document.body.scrollHeight)
		document.getElementById('main').onclick = function(ev) {
			ev = ev || event;
			if (is_black(ev.target.classList)) {
				create_row()
				makeSound(song[create_index])
				create_index++
				ev.path[1].style.height = "0px"
				setTimeout(function() {
					ev.path[2].removeChild(ev.path[1])
				}, 500)
				if (index >= allNum + 6) {
					setTimeout(function() {
						alert("win");
						clearInterval(gametime)
					}, 500)
				}
			} else {
				GG(ev.target);
			};
		};
	}

	function is_black(classList) {
		if (classList.value.indexOf("black") != -1) {
			return true
		}
		return false
	}

	function GG(eve) {
		clearInterval(gametime)
		if(eve.className!="cell")return
		eve.className += " error_flash"
		document.getElementById('main').onclick=null
		setTimeout(function() {
			alert("输啦");
			document.getElementById("start").style.display = "";
			document.getElementById("con").innerHTML = ""
		}, 2000)
	}

	function setContent(){
	    if(!ctx){
	        ctx = new AudioContext()
	    }
	}
	function makeSound(index){
	    // 获得音频上下文
	    setContent();
	    //得到音频振荡器
	    var osc = ctx.createOscillator();
	    //得到音量控制对象
	    var g = ctx.createGain();
	    // 连接振荡器和音量控制对象
	    osc.connect(g);
	    osc.type = 'sine';
	    osc.frequency.value = sounds[index];
	    var duration = 1;
	    g.connect(ctx.destination);
	    g.gain.value = 0;
	    osc.start();
	    g.gain.linearRampToValueAtTime(0.6,ctx.currentTime + 0.01)
	    osc.stop(ctx.currentTime + duration);
	    g.gain.exponentialRampToValueAtTime(0.01 , ctx.currentTime + duration)
	}
</script>

let song=[0,0,2,0,0,2]这一行是谱子
从0到20分别是从低音dao到高音si
[0,0,2,0,0,2]其实是葫芦娃的不知道家人们听出来没(乐
把葫芦娃完整地附上吧
[0, 0, 2, 0, 0, 2, 5, 5, 5, 4, 5, 4, 0, 2]

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨笨2019

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值