javascript(html5 canvas)做的拼图游戏,简单易懂

趁着摸鱼的时间,写了个拼图小游戏,操作简单、提神醒脑

效果图(最后的完成了好像被遮住了,哈哈)

实现思路

  1. 用到canvas的drawImage方法,使用8个参数的方式来将大图片切成一张张的小图,同时将每个小图片存储到对应的数组中。
  2. 在切图的同时用两个数组keys和orig_keys存取小图的下标,9张小图的话(keys就输数字0-8的数组)。
  3. 点击开始按钮的时候keys随机排序,然后从小图片数组中去获取对应的图片对象,更新其显示位置,这样就可以打乱图片了。
  4. 选择一个小图片(再次点击此图片会取消当前选择状态),再选择另一个图片将会互相交换位置。
  5. 每次交换位置后会对数组keys和orig_keys进行比较,如果两个数组结构一样,则表示拼图完成。

drawImage使用

可以看这篇文章

编写ImageDraw构造函数

//图片对象ImageDraw构造函数
	function ImageDraw(o,obj){
		this.id='',
		this.image=0,//图片对象(必填)
		this.sx=0,//图片切片开始x位置(显示整个图片的时候不需要填)
		this.sy=0,//图片切片开始y位置(显示整个图片的时候不需要填)
		this.sWidth=0, //图片切片开始宽度(显示整个图片的时候不需要填)
		this.sHeight=0,//图片切片开始高度(显示整个图片的时候不需要填)
		this.dx=0, //图片目标x位置(必填)
		this.dy=0, //图片目标y位置(必填)
		this.dWidth=0,//图片目标显示宽度(宽度不缩放时不必填)
		this.dHeight=0//图片目标高度高度(高度不缩放时不必填)
		
		this.init(o,obj);
	}
	ImageDraw.prototype.init=function(o,obj){
		this.lol=obj;
		for(var key in o){
			this[key]=o[key];
		}
		return this;
	}
	ImageDraw.prototype.render=function(context){
		draw(context,this);
		function draw(context,obj) {
			var ctx=context;
			ctx.save();
			
			if(!obj.image || getType(obj.dx)=='undefined' || getType(obj.dy)=='undefined'){
				throw new Error("绘制图片缺失参数");	
				return;
			} 
			ctx.translate(obj.dx,obj.dy);
			if(getType(obj.sx)!='undefined' && getType(obj.sy)!='undefined' && obj.sWidth && obj.sHeight && obj.dWidth && obj.dHeight){
				//裁剪图片,显示时候有缩放
				ctx.drawImage(obj.image, obj.sx, obj.sy, obj.sWidth, obj.sHeight, 0, 0, obj.dWidth, obj.dHeight);
			}else if(obj.dWidth && obj.dHeight){
				ctx.drawImage(obj.image, 0, 0, obj.dWidth, obj.dHeight);//原始图片,显示时候有缩放
			}else{
				ctx.drawImage(obj.image,0, 0);//原始图片,显示时候无缩放
			}
			ctx.restore();
		}
	}
	ImageDraw.prototype.isPoint=function(pos){
		//鼠标位置的x、y要分别大于dx、dy 且x、y要分别小于 dx+dWidth、dy+dHeight
		if(pos.x>this.dx && pos.y>this.dy && pos.x<this.dx+this.dWidth && pos.y<this.dy+this.dHeight ){//表示处于当前图片对象范围内
			return true;
		}
		return false;
	}
	

对图片进行切片

  1. 用大图片的长除以列数、用大图片的宽除以行数,分别得到小图片的长和宽;
  2. 用双循环初始化小图片
  3. keys存储key和posArr数组存取dx、dy(打乱的时候要用)
//对图片进行切片
	Jigsaw.prototype.sliceImage=function(){
		var imgObj = this.imgObj;
		var img,image,key=this.imageIndex,renderArr=this.renderArr;
		var countX=this.countX,countY=this.countY;
		
		image=imgObj[key];
		
		var disX = this.w/countX,//小图片的宽
			disY = this.h/countY;//小图片的高
			
		//相关数组清空处理	
		renderArr.length=0;
		this.orig_keys.length=0;
        this.keys.length=0;
        this.posArr.length=0;
		    
		//初始化小图片对象        			
		var x=y=n=0;	
		for(var i=0;i<countX;i++){
			x = i*disX;
			for(var j=0;j<countY;j++){
				y = j*disY;
				img = new ImageDraw({image:image,sx:x,sy:y,sWidth:disX,sHeight:disY, dx:x, dy:y ,dWidth:disX,dHeight:disY},this);
				renderArr.push(img);
				
				img.key=n;//设置key值
				img.keyPos=n;//设置所处于的位置
				
				this.orig_keys.push(n);
		        this.keys.push(n);
		        this.posArr.push({x:x,y:y})
		        n++;
			}
		}
	}

随机排序

	this.keys.sort(function(a,b){//随机排序
			return Math.random()>0.5?1:-1;
		})
  1. 打乱前keys是这样:[0, 1, 2, 3, 4, 5, 6, 7, 8]
  2. 打乱后keys是这样的 : [5, 0, 7, 1, 4, 8, 6, 2, 3]
  3. 循环小图片对象的时候下标是0,1,2这样开始的,那第一次取到的key是keys数组的第一个元素5
  4. 用5再去posArr取到第6个元素posArr[5],并把这个元素的dx,dy设置给当前下标为0的小图片,其他一样道理,这样就打乱了图片。
		var key,pos;   
		//排序后重新更改位置,达到打乱图片的效果     
		_.each(this.renderArr,function(item,index){
			if(item){
				key = that.keys[index];
				pos = that.posArr[key];
				//改变dx,dy既可
				item.dx=pos.x;
				item.dy=pos.y;
				item.image=that.imgObj[that.imageIndex];
				item.key=key;
				item.keyPos=index; 
			}
		});

看一下效果吧

给canvas添加点击事件,并给每个小图片增加判断,如果点击了当前小图片则增加一个边框,表示选中

Rect构造函数

function Rect(o){
		this.x=0,//x坐标
		this.y=0,//y坐标
		this.width=100,//宽
		this.height=40,//高
		this.thin=true,//线段薄一点
		
		this.init(o);
	}
	
	Rect.prototype.init=function(o){
		for(var key in o){
			this[key]=o[key];
		}
	}
	Rect.prototype.render=function(context){
		this.ctx=context;
		innerRender(this);
			
		function innerRender(obj){
			var ctx=obj.ctx;
			ctx.save()
			ctx.beginPath();
			ctx.translate(obj.x,obj.y);
			
			if(obj.lineWidth){
				ctx.lineWidth=obj.lineWidth;
			}
			if(obj.thin){
				ctx.translate(0.5,0.5);
			}
			ctx.rect(0,0,obj.width,obj.height);
			if(obj.fill){//是否填充
				obj.fillStyle?(ctx.fillStyle=obj.fillStyle):null;
				ctx.fill();
			}
			if(obj.stroke){//是否描边
				obj.strokeStyle?(ctx.strokeStyle=obj.strokeStyle):null;
				ctx.stroke();
			}	
		  	ctx.restore();
		}
	  	return this;
	}

点击后代码

//表示第一次选中
						item.selected=true;
						//添加一个边框用来指示被选择
						var rect = new Rect({
								x:item.dx,
								y:item.dy,
								width:item.dWidth,
								height:item.dHeight,
								stroke:true,
								strokeStyle:'skyblue'
						})
						rect.img=item;
						this.renderRectArr.push(rect);

此时效果

选中另一个图片后交换位置

  1. 清空选择状态及指示框
  2. 交换两个小图片对象的pos和key值
  3. 交换他们在keys数组中的值
  4. 判断此时keys与orig_keys是否一致,如果一致则表示完成
						var itemSelected = this.renderRectArr[0].img;
						//将已选择的dx、dy拷贝给x、y
						var x = itemSelected.dx,y=itemSelected.dy;
						//进行位置交换
						itemSelected.dx=item.dx,itemSelected.dy=item.dy,
						item.dx=x,item.dy=y;
						//清空选择状态及指示框
						itemSelected.selected=false;
						this.renderRectArr.length=0;
						
						//位置的交换
						var from_pos = itemSelected.keyPos;
						var from_key = itemSelected.key;
						var target_pos = item.keyPos;
						var target_key = item.key;
						
						itemSelected.pos=target_pos;
						itemSelected.key=target_key;
						item.pos=from_pos;
						item.key=from_key;
						//交换他们在this.keys中的位置
						this.keys.splice(from_pos,1,target_key);
						this.keys.splice(target_pos,1,from_key);
						
						//如果相等表示已经找好了
						if(this.diff(this.orig_keys,this.keys)){//每次交换完成以后都要进行一次判断,看是否完成拼图
							setTimeout(function(){
								alert("恭喜你已完成!");
								console.log('完成了');
							},0)
						}

最后、加上图片更换和难度系数的代码就完成了

var box = document.getElementById('box');
	var start = document.getElementById('start');
	jigsaw.init(box,start);
	
	function up(type){
		var el = document.getElementById("difficulty_"+type);
		
		if(el){
			var val=el.value;
			if(val<10){
				val++;
				el.value=val;
				if(type='col'){
					jigsaw.countY=val;
				}else{
					jigsaw.countX=val;
				}
			}
		}
	}
		
	function down(type){
		var el = document.getElementById("difficulty_"+type);
		
		if(el){
			var val=el.value;
			if(val>3){
				val--;
				el.value=val;
				if(type='col'){
					jigsaw.countY=val;
				}else{
					jigsaw.countX=val;
				}
			}
		}
	}
	
	var index=1;
	var small_img =document.getElementsByClassName("small_img")[0]; 
	document.getElementById("next").onclick=function(){
		var src = small_img.src;
		var reg = /(\d+)\.jpeg$/;
		var arr = reg.exec(src);
		
		if(arr.length>1){
			index = ++arr[1];
			if(index>16){
				index=1;
			}
			
			jigsaw.imageIndex=index;
			jigsaw.draw();
		}
		small_img.src='./images/'+index+'.jpeg';
	}

全部代码下载,无需积分

兄弟们给个三连吧,谢谢拉!!

猜你喜欢

转载自blog.csdn.net/dkm123456/article/details/114695468
今日推荐