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

前言

废话不多说,直接上源码

一、requestAnimationFrame是什么?


在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout/ setInterval 来实现,css3 可以使用 transition和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame。
原文详解链接:https://blog.youkuaiyun.com/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 = "<div id='BiuAll'></div>";
					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>

总结

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

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值