html+css+js实现飞机大战小游戏

前言

废话不多说,直接上源码

一、requestAnimationFrame是什么?


在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout/ setInterval 来实现,css3 可以使用 transition和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame。
原文详解链接:https://blog.csdn.net/qq_45890970/article/details/123576140

二、源码


<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<title></title>
	<style type="text/css">
		* {
			margin: 0;
			padding: 0;
			font-family: "Microsoft yahei", serif;
		}

		li {
			list-style-type: none;
		}

		body {
			overflow: hidden;
			user-select: none;
			/* 禁止选择文字 */
			-moz-user-select: -moz-none;
			/* 禁止鼠标右键复制 */
			-ms-user-select: none;
		}

		#box {
			position: relative;
			width: 512px;
			height: 768px;
			margin: 20px auto;
		}

		#map {
			overflow: hidden;
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			background: url("img/bg_1.jpg");
		}

		#level {
			position: absolute;
			top: 0;
			left: 0;
			z-index: 1;
			width: 100%;
			height: 100%;
		}

		#level h1 {
			font-size: 40px;
			padding-top: 60px;
			padding-bottom: 150px;
			line-height: 60px;
			text-align: center;
			color: #fff;
		}

		#level p {
			margin: 30px auto;
			width: 200px;
			height: 35px;
			line-height: 35px;
			text-align: center;
			background: #fff;
			font-weight: bolder;
			cursor: pointer;
		}

		#level p:hover {
			background: pink;
			color: #fff;
		}

		#map .plane,
		#map .biu,
		#map .enemy,
		#map .boom,
		#map .boom2 {
			position: absolute;
		}

		#map .plane {
			z-index: 8;
		}

		#map .biu {
			z-index: 10;
		}

		#map .boom2 {
			z-index: 11;
			animation: bling 2s 1;
			animation-fill-mode: forwards;
		}

		#map .enemy {
			z-index: 9;
		}

		#map .boom {
			z-index: 7;
			animation: fade .8s 1;
			animation-fill-mode: forwards;
		}

		/*爆炸后的消失效果*/
		/* @keyframes动画是循环的
				而transform 只执行一遍. */
		@keyframes fade {
			from {
				/* “1”完全不透明 */
				opacity: 1;
			}

			to {
				opacity: 0;
			}
		}

		@keyframes bling {
			0% {
				opacity: 1;
			}

			20% {
				opacity: 0;
			}

			40% {
				opacity: 1;
			}

			60% {
				opacity: 0;
			}

			80% {
				opacity: 1;
			}

			100% {
				opacity: 0;
			}
		}

		#score {
			display: none;
			position: absolute;
			top: 10px;
			left: 10px;
			color: #fff;
			line-height: 20px;
			font-size: 14px;
			font-weight: bold;
			/* z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。 */
			z-index: 20;
		}

		#restart {
			display: none;
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			z-index: 30;
		}

		#restart p {
			width: 300px;
			height: 40px;
			line-height: 20px;
			margin: 140px auto;
			color: #fff;
		}

		#restart p span {
			display: block;
			font-weight: bolder;
			font-size: 22px;
			text-align: center;
		}

		#restart .p1 span {
			color: red;
		}

		#restart .p2 span {
			color: #ffa80c;
		}

		#restart .p3 {
			font-family: "楷体";
			font-size: 20px;
			width: 100px;
			height: 35px;
			background: rgb(255, 255, 255);
			background: rgba(255, 255, 255, .8);
			color: #000;
			font-weight: bolder;
			line-height: 35px;
			text-align: center;
			border-radius: 3px;
			/* 设置浏览网页时鼠标光标的样式为手 */
			cursor: pointer;
		}

		#restart .p3:hover {
			background: white;
		}
	</style>
	<script type="text/javascript">
		window.onload = function () {
			/*
				告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
				该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
				注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()
			*/
			// requestAnimationFrame实现无限循环动画
			window.requestAnimationFrame = window.requestAnimationFrame || function (fn) {
				// 间隔时间为每秒60帧
				// 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
				// 一般来说,这个频率为每秒60帧
				return setTimeout(fn, 1000 / 60)
			};
			//方法用于取消以前通过对window.requestAnimationFrame()的调用计划的动画帧请求。
			window.cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;

			//定义全局变量,获取数据
			var oBox = document.getElementById("box"),
				oScore = document.getElementById("score"),
				oRe = document.getElementById("restart"),
				oLevel = document.getElementById("level"),
				oMap = document.getElementById("map"),
				oBiuAll = document.getElementById("BiuAll"),
				// 获取节点
				allBiu = oBiuAll.children,
				allReChild = oRe.children,
				// offsetTop是找距离定位父级左边的距离,没有定位则找body
				boxOffsetTop = oBox.offsetTop,
				// offsetLeft是找距离定位父级上边的距离
				boxOffsetLeft = oBox.offsetLeft;

			//启动
			exe();

			//初始选择难度界面的点击事件
			function exe() {
				//难度选择
				var aP = oLevel.getElementsByTagName("p");
				for (var i = 0, length = aP.length; i < length; i++) {
					(function (i) {
						aP[i].onclick = function (e) {
							e = e || window.event;
							startGame(i, {
								x: e.clientX - boxOffsetLeft,
								y: e.clientY - boxOffsetTop
							});//第一个实参为序号 ,第二个实参为存储着鼠标距离map边缘距离的json
						}
					})(i);
				}

				//restart按钮
				allReChild[2].onclick = function (ev) {
					// cancelAnimationFrame(oMap.bgTimer);//停止背景滚动
					oRe.style.display = "none";
					oLevel.style.display = "block";
					oScore.innerHTML = 0;
					oMap.innerHTML = "
"
; oBiuAll = document.getElementById("BiuAll"); allBiu = oBiuAll.children; }; } //开始游戏 function startGame(level, pos) { //执行 隐藏和清理 clearMap(); //执行 Map背景相关操作 MapBg(level); //执行 创建我军 var p = plane(level, pos); //执行 创建敌军 enemy(level, p); //enemy(level , plane(level , pos)); //得分清零 oBox.score = 0; } //隐藏和清理 function clearMap() { oScore.style.display = "block"; oLevel.style.display = "none";//隐藏关卡选择框 } //Map背景选择与运动 function MapBg(level) { oMap.style.backgroundImage = "url('img/bg_" + (level + 1) + ".gif')"; (function m() { oMap.bgPosY = oMap.bgPosY || 0; // oMap.bgPosY++;//背景移动 oMap.style.backgroundPositionY = oMap.bgPosY + 'px'; oMap.bgTimer = requestAnimationFrame(m); })(); } //创建我军 function plane(level, pos) { //创建我军图片 var oImg = new Image(); oImg.src = "img/plane_0.png"; oImg.width = 70; oImg.height = 70; oImg.className = "plane"; oImg.style.left = pos.x - oImg.width / 2 + 'px'; oImg.style.top = pos.y - oImg.height / 2 + 'px'; oMap.appendChild(oImg); //边界值 var leftMin = -oImg.width / 2, leftMax = oMap.clientWidth - oImg.width / 2, topMin = 0, topMax = oMap.clientHeight - oImg.height / 2; //加入mousemove事件(飞机移动) document.onmousemove = function (ev) { ev = ev || window.event; //获取飞机实时坐标,并限制边界值 var left = ev.clientX - boxOffsetLeft - oImg.width / 2; var top = ev.clientY - boxOffsetTop - oImg.height / 2; left = Math.max(leftMin, left); left = Math.min(leftMax, left); top = Math.max(topMin, top); top = Math.min(topMax, top); //赋值 oImg.style.left = left + 'px'; oImg.style.top = top + 'px'; }; //调用子弹函数 fire(oImg, level); return oImg; } //我军子弹 function fire(oImg, level) { oBox.biuInterval = setInterval(function () { if (oBox.score >= 500) { createBiu(true, -1); createBiu(true, 1); } else { createBiu(); } }, [100, 200, 200, 15][level]); function createBiu(index, d) { //创建子弹 var oBiu = new Image(); oBiu.src = "img/fire1.png"; oBiu.width = 30; oBiu.height = 30; oBiu.className = "biu"; var left = oImg.offsetLeft + oImg.width / 2 - oBiu.width / 2; var top = oImg.offsetTop - oBiu.height + 5; if (index) { left += oBiu.width * d } oBiu.style.left = left + "px"; oBiu.style.top = top + 'px'; oBiuAll.appendChild(oBiu); //子弹运动 function m() { if (oBiu.parentNode) { var top = oBiu.offsetTop - 20; if (top < -oBiu.height) { oBiuAll.removeChild(oBiu); } else { oBiu.style.top = top + 'px'; requestAnimationFrame(m); } } } //将运动执行队列放后面,不然子弹会直接初始就在 top-50 的位置 setTimeout(function () { requestAnimationFrame(m); }, 50); } } //创建敌军 function enemy(level, oPlane) { var w = oMap.clientWidth, h = oMap.clientHeight; var speed = [5, 6, 8, 8][level]; //敌军下落速度 var num = 1; oBox.enemyIntetval = setInterval(function () { var index = num % 30 ? 1 : 0; //生成敌军 var oEnemy = new Image(); oEnemy.index = index; oEnemy.HP = [20, 1][index]; oEnemy.speed = speed + (Math.random() * 0.6 - 0.3) * speed; oEnemy.speed *= index ? 1 : 0.5; oEnemy.src = "img/enemy_" + ["big", "small"][index] + ".png"; oEnemy.className = "enemy"; oEnemy.width = [104, 54][index]; oEnemy.height = [80, 40][index]; oEnemy.style.left = Math.random() * w - oEnemy.width / 2 + 'px'; oEnemy.style.top = -oEnemy.height + 'px'; oMap.appendChild(oEnemy); num++; //敌军运动 function m() { if (oEnemy.parentNode) { var top = oEnemy.offsetTop; top += oEnemy.speed; if (top >= h) { //漏掉飞机减分 oBox.score--; oScore.innerHTML = oBox.score; oMap.removeChild(oEnemy); } else { oEnemy.style.top = top + 'px'; //子弹碰撞检测 for (var i = allBiu.length - 1; i >= 0; i--) { var objBiu = allBiu[i]; if (coll(oEnemy, objBiu)) { //移除子弹 oBiuAll.removeChild(objBiu); oEnemy.HP--; if (!oEnemy.HP) { //打掉飞机加分 oBox.score += oEnemy.index ? 2 : 20; oScore.innerHTML = oBox.score; //敌军爆炸图 boom(oEnemy.offsetLeft, oEnemy.offsetTop, oEnemy.width, oEnemy.height, index ? 0 : 2); //移除敌军 oMap.removeChild(oEnemy); return; } } } //我军碰撞检测 if (oPlane.parentNode && coll(oEnemy, oPlane)) { //敌军爆炸图 boom(oEnemy.offsetLeft, oEnemy.offsetTop, oEnemy.width, oEnemy.height, index ? 0 : 2); //我军爆炸图 boom(oPlane.offsetLeft, oPlane.offsetTop, oPlane.width, oPlane.height, 1); //移除敌军 oMap.removeChild(oEnemy); //移除我军 oMap.removeChild(oPlane); GameOver(); return; } requestAnimationFrame(m); } } } requestAnimationFrame(m); }, [350, 150, 120, 40][level]); } //爆炸函数 function boom(l, t, w, h, i) { var oBoom = new Image(); oBoom.src = "img/" + ["boom_small", "plane_0", "boom_big"][i] + ".png"; oBoom.className = 'boom' + ["", "2", ""][i]; oBoom.width = w; oBoom.height = h; oBoom.style.left = l + "px"; oBoom.style.top = t + 'px'; oMap.appendChild(oBoom); setTimeout(function () { oBoom.parentNode && oMap.removeChild(oBoom); }, [1200, 2500, 1200][i]); } //两个物体 碰撞检测 function coll(obj1, obj2) { var T1 = obj1.offsetTop, B1 = T1 + obj1.clientHeight, L1 = obj1.offsetLeft, R1 = L1 + obj1.clientWidth; var T2 = obj2.offsetTop, B2 = T2 + obj2.clientHeight, L2 = obj2.offsetLeft, R2 = L2 + obj2.clientWidth; return !(B1 < T2 || R1 < L2 || T1 > B2 || L1 > R2); } //游戏结束 function GameOver() { document.onmousemove = null; //清除移动事件 clearInterval(oBox.biuInterval);//不再产生新子弹 clearInterval(oBox.enemyIntetval);//不再产生新敌军 restart(); } //结算+重新开始 function restart() { oScore.style.display = "none"; var s = oBox.score; var honor; if (s < -300) { honor = "闪避+MAX!!!"; } else if (s < 10) { honor = "菜得…算了我不想说了…"; } else if (s < 30) { honor = "抠脚侠!"; } else if (s < 100) { honor = "初级飞机大师"; } else if (s < 200) { honor = "渐入佳境"; } else if (s < 500) { honor = "中级飞机大师"; } else if (s < 1000) { honor = "高级飞机大师"; } else if (s < 5000) { honor = "终极飞机大师"; } else { honor = "孤独求败!"; } oRe.style.display = "block"; allReChild[0].children[0].innerHTML = s; allReChild[1].children[0].innerHTML = honor; } } </script> </head> <div id="box"> <div id="level"> <h1>飞机大作战</h1> <p>简单</p> <p>中等</p> <p>困难</p> <p style="color: #f00">开挂啊</p> </div> <div id="map"> <div id="BiuAll"></div> </div> <div id="score">0</div> <div id="restart"> <p class="p1">您的最终得分是:<span>0</span></p> <p class="p2">获得称号:<span>抠脚侠</span></p> <p class="p3">重新开始</p> </div> </div> </body> </html>

总结

以上就是今天所展示的内容,本文是我初学这门课程后做的一个实战小项目,欢迎何为大佬留言指点

你可能感兴趣的:(css,javascript,html,前端)