拼图游戏Canvas版

尹承业
2023-12-01

原身

一开始写的拼图是用div一个一个列出来的,里面放的是切好的图片。拼图小游戏(前端)

canvas版的思路

  • 游戏结束的判定与上一个版本一样,数组的顺序达到了它对应的位置,表示游戏结束。
  • 因为使用canvas,我们需要记录每块拼图在图片上的位置和大小,记录成数组
  • canvas的使用思路HTML5 Canvas学习笔记(6)拼图游戏(数字版)

canvas方法

canvas.drawImage()使用可以参照drawImage()方法

拼图对象

这里生成拼图对象传入了两个数组。
一个是canvas的九格位置数组(不可改动)canvas九格的每一格都可以放图片的任意一个图片碎片或者“白块”,以此实现“拼图”的效果;
一个是图片的九格位置数组(可打乱);

代码部分

样式没有过多调整,能用就成

HMTL

 <!-- 流程1,选择想拼的图片 -->
   流程1,选择想拼的图片
   <div class="process1">
       <div>选择图片</div>
       <input class="file" type="file" id="ipt">
   </div>
   <!-- 流程2:切图 -->
   流程2:切图
   <div class="process2">
       <div class="img">
           <div class="mask">
           </div>
       </div>
       <button>确定</button>
   </div>
   <div class="process3">
       <canvas id ="canvas" width="500" height="500"></canvas>
   </div>

css

<style>
       .process1{
           width: 150px;
           height: 80px;
           position: relative;
           background: #000;
           color: #fff;
           text-align: center;
           line-height: 80px;
       }
       .file{
           /* visibility: hidden; */
           opacity: 0;
           width: 150px;
           height: 80px;
           position: absolute;
           left: 0;
           top: 0;
       }
       .img{
           margin: 0 auto;
           position: relative;
           overflow: hidden;
           width: 800px;
       }
       .mask{
           position: absolute;
           width: 30%;
           background-color:rgba(0, 0, 0, 0);
           box-shadow: 0 0 0 10000px rgba(0,0,0,.5);
           left: 30%;
           top: 30%;
       }
       canvas{
           border: 1px solid #000;
       }
   </style>

js

var ipt = document.getElementById('ipt')
       var canvas = document.getElementById('canvas')
       var contDv = document.getElementsByClassName('img')[0];
       var  c_msg
       var sqrt = document.getElementsByClassName('mask')[0];
       sqrt.style.height = sqrt.offsetWidth + 'px'
       var ctx = canvas.getContext('2d');
       var image = new Image();
       var sx=0,sy=0;
       var step = 500/3
       var imagestep;
       var rate ;
       //选择一张图片,生成img对象,流程一
       ipt.onchange = function(e){
           var windowurl = window.URL || window.webkitURL;
           var url =  windowurl.createObjectURL(e.target.files[0]);
           // img.src  = url
           contDv.style.background = `url(${url}) no-repeat center center/100% 100%`;
           image.src = url;
           image.onload = function(){
               var ix = this.width;
               var iy = this.height;
               contDv.style.height = contDv.offsetWidth *iy/ix + 'px'
               rate = iy/contDv.offsetHeight;
               c_msg = contDv.getClientRects()[0];
           }
       }
       // 确定裁剪区域,流程二,思路可以参照放大镜的思路,这部分有问题,但是因为目的不是它就没管
       var bdy = document.documentElement
       sqrt.onmousedown = function(e){
           var msg = this.getClientRects()[0];
           // 计算鼠标与白块的距离
           var space_x = e.pageX - msg.left;
           var space_y = e.pageY - msg.top;
           var _this = this;
           var max_x = c_msg.width-this.offsetWidth;
           var max_y = c_msg.height-this.offsetHeight;
           var scrolltop = bdy.scrollTop;
           var scrollleft = bdy.scrollLeft;
           document.onmousemove = function(eve){
               sx = eve.pageX+ scrollleft - space_x - c_msg.left;
               sy = eve.pageY + scrolltop- space_y - c_msg.top;
               sx = sx>max_x?max_x:sx
               sy = sy>max_y?max_y:sy
               _this.style.left = sx<0?0:sx + 'px';
               _this.style.top = sy<0?0:sy + 'px';
           }
       }
       sqrt.onmouseup = function(){
           document.onmousemove = ''
       }
       // 开始裁剪
       var btn = document.getElementsByTagName('button')[0];
       var jigsaw
       btn.onclick = function(){
           imagestep = sqrt.offsetWidth*rate/3;//获得每块拼图的宽高
           var pointlist = [];
           var canvaslist =[]
           var pointy=sy*rate,
               cy=0,
               pointx,
               cx;
           for(var i = 0 ; i <3 ;i ++){
               if(i!==0){pointy+=imagestep;cy+=step}
               pointx=sx*rate,cx=0
               for(var j = 0 ;j<3;j++){
                   if(j!==0){pointx+=imagestep;cx+=step}
                   console.log(pointx,pointy)
                   pointlist.push({x:pointx,y:pointy,id:i*3+j})//id当做判断游戏结束的依据
                   canvaslist.push({x:j,y:i})
               }
           }
           console.log(canvaslist,pointlist)
           jigsaw = new Jigsaw('canvas',pointlist,canvaslist,9,image,imagestep);
           jigsaw.init()
       }

拼图对象


function random (n){
    return Math.ceil(Math.random()*n)
}
//这里传入两个数组,一是游戏的九各位置,一个是图片碎片的位置
function Jigsaw(dom,imgdata,canvasdata,size,image,imagestep){
    this.$data = imgdata;//原始数据
    this.$canvaslist = canvasdata;//canvas九宫数据
    this.$size =size;//九宫格或者十六宫格
    this.$jigsaw_data = null //处理后的拼图数组
    this.$dom = document.getElementById(dom)//承载游戏的元素
    this.$ctx = this.$dom.getContext('2d');//画布
    this.$finish = false;//游戏结束标志
    this.$change;//白块所在元素的索引值
    this.$image = image;//图片
    this.$step = 0;//移动步数
    this.$imagestep = imagestep
}
Jigsaw.prototype.init = function(){
    if(!this.$dom){
        console.log('要承载的对象不存在')
        return
    }
    if(this.data_handle()){//处理传入的数组
        console.log('错误的数据格式')
        return
    }
    this.game()
    this.interactive()
}
Jigsaw.prototype.data_handle = function(){
    if(this.$data.length!=this.$size){
        return true //传入的数据长度不对
    }
    var newarr=[];
    var arr = this.$data;
    var last = arr.pop()
    for( var i =7 ; i>=0;i--){
        var tmp = arr.splice(random(i),1)
        newarr = newarr.concat(tmp)
    }//将传入的原始数据打乱
    newarr.push(last);//把空白拼图放在数组最后一位
    this.$change = newarr.length -1;//记录空白拼图的位置
    newarr[this.$change].white = true
    this.$jigsaw_data = newarr
}
Jigsaw.prototype.createLine = function(){
    this.$ctx.clearRect(0,0,this.$dom.offsetWidth,this.$dom.offsetHeight)//将画布清空
    this.$jigsaw_data.forEach((item,y)=>{
        var p = this.$canvaslist[y]
        if(!item.white){
            this.$ctx.drawImage(this.$image,item.x,item.y,this.$imagestep,this.$imagestep,p.x*step,p.y*step,step,step)//如果当前对象不是空白拼图
        }
        this.$ctx.rect(p.canvasx,p.canvasy,step,step);
        this.$ctx.stroke()
    })
}
Jigsaw.prototype.exchange = function(obj){
    var _this = this;
    var x = Math.floor(obj.x/step);
    var y = Math.floor(obj.y/step);//计算鼠标落在哪块拼图上
    var index = y*3+x//计算出被点的区域对应索引值
    console.log(obj,index)
    var $x = this.$canvaslist[index].x - this.$canvaslist[this.$change].x;
    var $y = this.$canvaslist[index].y - this.$canvaslist[this.$change].y;
    if(Math.abs($x+$y)==1){//计算鼠标所落的拼图是否与白色拼图相邻,是则交换
        var tmp = _this.$jigsaw_data[index];
        _this.$jigsaw_data[index] =  _this.$jigsaw_data[this.$change];
        _this.$jigsaw_data[this.$change]=tmp;
        this.$change = index
        _this.game()
        _this.$step ++
    }
}
Jigsaw.prototype.resetwhite=function(){
    this.$change = ''
}
Jigsaw.prototype.interactive = function(){
    var _this = this
    this.$dom.onclick = function(e){
        var x = e.pageX - this.offsetLeft
        var y = e.pageY - this.offsetTop
        if(_this.$finish){
            console.log('游戏结束,你一共走了' + _this.$step  + '步')
            return 
        }else{
            _this.exchange({x:x,y:y})
        }
    }
}
Jigsaw.prototype.game = function(){
    this.createLine();
    this.endjudge();
    if(this.$finish){
        console.log('游戏结束,你一共走了' + this.$step  + '步')
    }
}
Jigsaw.prototype.endjudge=function(){
   for(var i = 0 ; i <this.$jigsaw_data.length ;i ++){
       var obj = this.$jigsaw_data[i]
       if(obj&&obj.id !==i){
           this.$finish = false;
           break;
       }else{
        this.$finish = true;
       }
   }
}
 类似资料: