趁着摸鱼的时间,写了个拼图小游戏,操作简单、提神醒脑
效果图(最后的完成了好像被遮住了,哈哈)
实现思路
- 用到canvas的drawImage方法,使用8个参数的方式来将大图片切成一张张的小图,同时将每个小图片存储到对应的数组中。
- 在切图的同时用两个数组keys和orig_keys存取小图的下标,9张小图的话(keys就输数字0-8的数组)。
- 点击开始按钮的时候keys随机排序,然后从小图片数组中去获取对应的图片对象,更新其显示位置,这样就可以打乱图片了。
- 选择一个小图片(再次点击此图片会取消当前选择状态),再选择另一个图片将会互相交换位置。
- 每次交换位置后会对数组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;
}
对图片进行切片
- 用大图片的长除以列数、用大图片的宽除以行数,分别得到小图片的长和宽;
- 用双循环初始化小图片
- 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;
})
- 打乱前keys是这样:[0, 1, 2, 3, 4, 5, 6, 7, 8]
- 打乱后keys是这样的 : [5, 0, 7, 1, 4, 8, 6, 2, 3]
- 循环小图片对象的时候下标是0,1,2这样开始的,那第一次取到的key是keys数组的第一个元素5
- 用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);
此时效果
选中另一个图片后交换位置
- 清空选择状态及指示框
- 交换两个小图片对象的pos和key值
- 交换他们在keys数组中的值
- 判断此时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';
}