使用js编写一个拼图游戏

使用js编写一个拼图游戏

目标

使用原生js编写一个拼图游戏,我这里写了两种拼图的方法。一种是拖拽拼图,一种是经典的九宫格拼图,可以自定义参数设置游戏难度

先看看截图效果

拖拽模式(拖拽图片切换图片)
在这里插入图片描述
点击模式(点击图片与空白区域切换位置)
在这里插入图片描述
不多说,直接上代码

css

	#canvasBox{
		margin: 0 auto;
		position: fixed;
		border: 2px solid #f00;
		overflow: hidden;
	}
	.item{
		display: inline-block;
		border: 1px solid #f00;
		position: absolute;
		top: 0;
		left: 0;
		transition: 0.1s;
	}

html

	<div style="margin: 0 auto;text-algin:center;">
		<button onclick="setGame('block', 'none')">拖拽模式</button>
		<button onclick="setGame('none', 'block')" >点击模式</button>
	</div>
	
	<div id="canvasBox"></div>
	<div id="canvasBox2"></div>

javascript

/*
			 * @title JigsawGame 拼图游戏
			 * @params obj Object 游戏参数
			 * {
			 * 	@param Id String 容器id
			 * 	@param imgUrl String 图片路径
			 * 	@param level Number 游戏难度 简单:1 || 普通:2 || 困难:3
			 * 	@param gameType Number 游戏类型  拖动版本:1 || 点击版本:2
			 * }
			 * @author beideng
			 */
			function JigsawGame(obj){

				// 初始化容器
				this.Id = document.getElementById(obj.Id);

				// 初始化图片
				this.img = new Image();
				this.img.src = obj.imgUrl;

				// 容器最大宽度
				this.windowWidth = document.body.clientWidth;
				this.maxWidth = this.windowWidth > 750 ? 750 : (this.windowWidth * 0.9);

				// 设置容器宽高
				this.Id.style.width = this.maxWidth + "px";
				this.Id.style.height = this.maxWidth + "px";
				this.Id.style.left = (this.windowWidth - this.maxWidth)/2 + "px";
				this.Id.style.top = 50 + "px";

				// 获取容器范围
				this.boxOffsetY = parseFloat(this.Id.style.top);
				this.boxOffsetX = parseFloat( this.Id.style.left);

				// 关卡(简单:1 || 普通:2 || 困难:3)
				if(obj.level == 1 || obj.level == 2 || obj.level == 3 ){
					this.Level = obj.level;
				}else{
					this.level = 1;	
				}

				// 难度
				var diffArr = [3, 4, 5];
				this.Diff= diffArr[this.Level-1];

				// canvas宽高
				this.cW = this.maxWidth/ this.Diff;
				this.cH = this.maxWidth/ this.Diff;

				// 记录的小方块个数
				this.number = 0;
				// 正确的数组
				this.numberArr = [];
				// 存储小方块的中心点坐标
				this.centerXY = [];

				/*
				 * 获取游戏类型
				 */
				this.gameType = obj.gameType || 1;

				// 记录最后一个元素的标记
				this.lastElement = {
					sign: 0,
					left: 0,
					top: 0,
					Id: obj.Id + 1
				};



				// 初始化
				this.Init();

			}

			/*
			 * 操作方法 *
			 */
			JigsawGame.prototype = {
				
				/*
				 * @method 初始化 
				 */
				Init: function(){

					var that = this;

					this.img.onload = function(){

						// 格子宽高
						var LevelW = that.img.width/that.Diff;
						var LevelH = that.img.height/that.Diff;

						for(var i = 0 ; i < that.Diff; i++){
							for(var j = 0 ; j < that.Diff; j++){
								// 初始化小方块
								that.initCube(i, j, LevelW, LevelH);
							}
						}

						// 打乱小方块
						that.upsetElement();

						// 游戏类型判断
						if(that.gameType == 1){
							// 监听拖动
							that.Id.addEventListener("mousedown",function(event){
								that.mouseDown(event);
							}, false);
						}else{
							
							// 获取空白小方块坐标
							that.getLastElement();

							// 监听点击
							that.Id.addEventListener("click",function(event){
								that.mouseClick(event);
							}, false);
						}

					}

				},

				/*
				 * @method 初始化小方块 
				 * @param i Number 循环值
				 * @param j Number 循环值
				 * @param j LevelW 小方块宽
				 * @param j LevelH 小方块高
				 */
				initCube: function(i, j, LevelW, LevelH){

					// 创建一个小方块
					var item = document.createElement("div"),
						cW = this.cW,
						cH = this.cH;
					
					item.className = "item";
					item.setAttribute("data-index", this.number);
					item.style.width = cW + "px";
					item.style.height = cH + "px";
					item.style.left = i * cW + "px";
					item.style.top = j * cH + "px";
					item.innerHTML = "<canvas class='' width='"+ cW +"' height='"+ cH +"'></canvas>";
					this.Id.appendChild(item);

					var canvas = item.querySelector("canvas");
					var ctx = canvas.getContext("2d");
					
					/*
					 * 当游戏为点击类型时
					 * 去掉最后一个小方块里的图片
					 * 且记录当前元素的坐标以及编号
					 */ 
					if(this.gameType != 1 && j == this.Diff-1 && i == this.Diff-1){
						this.lastElement.sign = this.number;
						item.id = this.lastElement.Id;

					}else{
						ctx.drawImage(this.img, i * LevelW, j * LevelH , LevelW, LevelH, 0 , 0, cW, cH)
					}
					
					// 每添加一个就压入一次到数组
					this.numberArr.push({
						x: i*cW +"px" ,
						y: j*cH +"px"
					});
					this.number++;

					// 压入初始中心点
					this.centerXY.push({
						x: i*cW + cW / 2,
						y: j*cH + cH / 2
					});

					
				},

				/*
				 * @method 悬停拖住小方块
				 * @param event Object 鼠标对象
				 */
				mouseDown: function(event){
					console.log(event)

 					var event = event || window.event;

 					var that = this;

          var target = event.target || event.srcElement;

          // 保证拖动的是想要的元素
          if( target.parentElement.className.toLowerCase() == "item"){
						
						var Element = target.parentElement;

						// 存储当前元素的top,left
						var thisTop = parseFloat( Element.style.top );
						var thisLeft = parseFloat( Element.style.left );

						// 获取当前点击的位置
						var pageX = event.pageX;
						var pageY = event.pageY;
						
						// 拖动
						document.onmousemove = function(e){
							console.log(e)
							that.mouseMove(e, Element, thisTop, thisLeft, pageY, pageX);
							
							return false;
						}
						
						// 松开
						document.onmouseup = function(e){

							that.mouseUp(e, Element, thisTop, thisLeft)
							
							// 释放拖拽
							document.onmousemove = null;
							document.onmouseup = null;
							return false;
						}
          }

          return false;
				},

				/*
				 * @method 拖动小方块
				 * @param e Object 鼠标对象
				 */
				mouseMove: function(e, Element, thisTop, thisLeft, pageY, pageX){
 					var pageX2 = e.pageX;
					var pageY2 = e.pageY;
					Element.style.top = thisTop + (pageY2 - pageY) + "px";
					Element.style.left = thisLeft + (pageX2 - pageX) + "px";
					Element.style.zIndex = 1000;
				},

				/*
				 * @method 松开小方块
				 * @param e Object 鼠标对象
				 */
				mouseUp: function(e, Element, thisTop, thisLeft){

 					var that = this,
		 					cW = this.cW,
							cH = this.cH;

 					// 检测当前拖动替换目标
					var moveCenterX = parseFloat(Element.style.left) + cW / 2;
					var moveCenterY = parseFloat(Element.style.top) + cH / 2;
					var changeElementIndex = this.checkChangeElement(moveCenterX, moveCenterY);
					var changeElement = this.Id.getElementsByClassName("item")[changeElementIndex];
			
					// 限制拖拽范围
					// 当松开的坐标xy在容器范围内
					if( e.pageX < this.boxOffsetX || e.pageX > (this.boxOffsetX + this.maxWidth) || e.pageY < this.boxOffsetY || e.pageY > (this.boxOffsetY + this.maxWidth) ){
						console.log("释放")

						Element.style.top = thisTop  + "px";
						Element.style.left = thisLeft + "px";
						
					}else{

						// 判断当前元素是否离开了自己的格子
						if( Element.getAttribute("data-index") == changeElement.getAttribute("data-index")){
							Element.style.top = thisTop  + "px";
							Element.style.left = thisLeft + "px";
						}else{

							// 进行替换
							Element.style.top = changeElement.style.top ;
							Element.style.left = changeElement.style.left ;

							changeElement.style.top = thisTop + "px";
							changeElement.style.left = thisLeft + "px";
							changeElement.style.zIndex = 1000; 

							// 更新小方块中心点
		        	this.updateElement();
						}

					}

					// 消除层级问题
					setTimeout(function(){
						Element.style.zIndex = 0;
						changeElement.style.zIndex = 0; 
						if(that.compareArray()){
							alert("恭喜你,拼图成功!");
						}
					}, 150);
					
					// 判断拼图完成
					console.log(this.compareArray())
					console.log(this.numberArr)

				},

				/*
				 * @method 检测当前拖动替换目标
				 * @param moveLeft Number 鼠标移动的x值
				 * @param moveTop Number 鼠标移动的y值
				 * @return minIndex Number 返回目标对象下标
				 * 通过三角函数检测当前拖动对象中心点和其他所有对象中心点距离,离谁最近就和谁替换
				 */
				checkChangeElement: function(moveLeft, moveTop){

					// 最小距离
					var minDistance = null; 
					// 最小距离替换目标
					var minIndex = null;

					for(var i = 0 ; i < this.centerXY.length; i++){

						var x = Math.abs( moveLeft - this.centerXY[i].x ); 
						var y= Math.abs( moveTop - this.centerXY[i].y ); 
						var val = Math.ceil(Math.sqrt( x * x +  y * y));
						
						// 初次判断
						if(minDistance == null){
							minDistance = val;
							minIndex = i;
						} 
						// 后续判断
						if(minDistance > val){
							minDistance = val;
							minIndex = i;
						}

					}
		
					// 返回目标对象下标
					return minIndex;
				},

				/*
				 * @method 更新小方块中心点
				 */
				updateElement: function(){
					var allElement = this.Id.getElementsByClassName("item"),
							cW = this.cW,
							cH = this.cH;
					this.centerXY = [];

					for(var i = 0 ; i < allElement.length; i++){
						this.centerXY.push({
							x: parseFloat(allElement[i].style.left) + cW / 2,
							y: parseFloat(allElement[i].style.top) + cH / 2
						});
					}
				},

				/*
				 * @method 点击小方块
				 * @param event Object 鼠标对象
				 * @ 1、点击当前非空白小方块
				 * @ 2、获取其坐标,并加减一个一个方块宽度,用这个加减坐标去检索空白小方块是否在目标小方块周边
				 * @ 3、如果在,则替换这两个小方块的坐标
				 */
				mouseClick: function(event){
					console.log(event)

 					var event = event || window.event;

 					var that = this;

          var target = event.target || event.srcElement;

          // 保证拖动的是想要的元素
          if( target.parentElement.className.toLowerCase() == "item"){
						
						var Element = target.parentElement;
						
						// 当当前点击目标为空白小方块时,终止函数
						if(Element.getAttribute("data-index") == this.lastElement.sign){
							return ;
						}


						// 存储当前元素的top,left
						var thisTop = parseFloat( Element.style.top );
						var thisLeft = parseFloat( Element.style.left );
						
						// 点击检测空白方块是否在当前对象周边
						if(this.mouseClickCheck(thisTop, thisLeft)){
							console.log(222)
							// 获取空白元素
							var lastElement = document.getElementById(this.lastElement.Id);
							
							// 替换这两个元素的坐标
							Element.style.top = lastElement.style.top;
							Element.style.left = lastElement.style.left;

							lastElement.style.top = thisTop + "px";
							lastElement.style.left = thisLeft + "px";

							this.lastElement.left = thisLeft ;
							this.lastElement.top = thisTop;
						

							// 消除层级问题
							setTimeout(function(){
								if(that.compareArray()){
									alert("恭喜你,拼图成功!");
								}
							}, 150);
							
							// 判断拼图完成
							console.log(this.compareArray())
							console.log(this.numberArr)

						}
						
          }

          return false;
				},

				/*
				 * @method 点击检测空白方块是否在当前对象周边
				 * @param thisTop Number 当前点击元素的top
				 * @param thisLeft NUmber 当前点击元素的left
				 * @return Boolean 是否在周边
				 */
				mouseClickCheck: function(thisTop, thisLeft){

					var cW = this.cW,
							cH = this.cH;

					if(thisTop == this.lastElement.top && (thisLeft - cH) == this.lastElement.left){
						return true;
					}

					if(thisTop == this.lastElement.top && (thisLeft + cH) == this.lastElement.left){
						return true;
					}

					if((thisTop - cW) == this.lastElement.top && thisLeft == this.lastElement.left){
						return true;
					}

					if((thisTop + cW) == this.lastElement.top && thisLeft == this.lastElement.left){
						return true;
					}
	
					return false;
				},

				/*
				 * @method 获取空白元素left,right
				 */
				getLastElement: function(){

					// 获取空白元素
					var lastElement = document.getElementById(this.lastElement.Id);
					console.log(this.lastElement);
					this.lastElement.left = parseFloat(lastElement.style.left) ;
					this.lastElement.top = parseFloat(lastElement.style.top);
				},

				/*
				 * @method 打乱小方块
				 * 以小方块的个数为次数,每次随机抽取两个小于小方块的数,然后替换两个dom元素的定位坐标
				 */
				upsetElement: function(){

					for (var i = 0; i < this.number-1; i++) {

						// 获取两个不相等的随机值
						var n1 = Math.floor(Math.random()*this.number);
						var n2 = Math.floor(Math.random()*this.number);
						do{
							n2 = Math.floor(Math.random()*this.number);
						}while(n1 == n2)

						// 替换当前的两个小方块的坐标
						var allElement = this.Id.getElementsByClassName("item");
						var Top = allElement[n1].style.top ;
						var Left = allElement[n1].style.left ;
						
						allElement[n1].style.top = allElement[n2].style.top ;
						allElement[n1].style.left = allElement[n2].style.left ;
						
						allElement[n2].style.top = Top ;
						allElement[n2].style.left = Left ;

					}

				},

				/*
				 * @method 比较小方块是否拼图完成
				 * @return boolean
				 * 获取切换小方块后,获取小方块的序号并与正确排序数组进行比较
				 */
				compareArray: function(){
					// 获取序号
					var allElement = this.Id.getElementsByClassName("item");

					for(var i = 0; i < this.number-1; i++){
						// 比较序号
						if( this.numberArr[i].x != allElement[i].style.left ||  this.numberArr[i].y != allElement[i].style.top ){
							return false;
						}
					}
					
					return true;
				},

			}

			// 实例化一个对象
			var box = new JigsawGame({
					Id: 'canvasBox',
					imgUrl: '../image/lingtai.jpg',
					level: 1,
					gameType: 1
			});

			// 实例化一个对象
			var box2 = new JigsawGame({
					Id: 'canvasBox2',
					imgUrl: '../image/lingtai.jpg',
					level: 1,
					gameType: 2
			});

			function setGame(a, b){
					document.getElementById("canvasBox").style.display = a;
					document.getElementById("canvasBox2").style.display = b;
			}
			setGame("block", "none");

稍微修改一下样式和触发事件,就是一个h5版本的demo。由于没用到项目里,没有考虑兼容问题

猜你喜欢

转载自blog.csdn.net/qq_36990322/article/details/87781677
今日推荐