你的位置:首页 > Java教程

[Java教程]JavaScript写一个拼图游戏


  拼图游戏的代码400行, 有点多了, 在线DEMO的地址是:打开;

  因为使用canvas,所以某些浏览器是不支持的: you know;  

  为什么要用canvas(⊙o⊙)?  因为图片是一整张jpg或者png, 我们要用把图片导入到canvas画布, 然后再调用上下文context的getImageData方法, 把图片处理成小图, 这些小图就作为拼图的基本单位;

  如何判断游戏是否结束, 或者说如何判断用户拼成的大图是正确的? 我们就要在刚刚生成的小图上面添加自定义属性, 后期在小图被移动后再一个个判断,如果顺序是对的, 那么这张大图就拼接成功, 允许进入下一关;

  

  游戏一共有四个关卡, 不会有人通关的,真的....因为第四关把图片的宽高分别切成了6份, 看着都晕好吧(∩_∩);

    

  

  因为要考虑到移动端的效果, 所以主界面图片是根据屏幕适配, 拼图大图的大小是屏幕宽度和屏幕高度之间最小值的一半, 都是为了不出现滚动条。 比如:用户的手机是横屏模式, 这个横屏的宽度是1000px,高度是300px, 如果我们把主图片的宽设置为屏幕1000px的一半500, 那么垂直方向就出滚动条了;

  用户的事件只要考虑上下左右四个方向键即可, 要判断图片是否可以移动, 也要考虑到当图片移动的时候的动画效果 ,感兴趣的话考虑我的实现, 和我写的2048是一样的道理;2048的DEMO;

  如果用户觉得这些图片不好看, 甚至可以上传自己手机的图片, 浏览器要支持FileReader的API, 移动是基于webkit的内核,可以不用考虑兼容性;

 

  代码包含工具方法和一些基本的配置, 比如, 图片地址的配置, 图片要切成的块数 , 加载图片的工具方法等:

    //游戏关卡的图片和游戏每一个关卡要切成的图片快个数    var levels = ["lake.jpg","cat.jpg","follower.jpg","view.jpg"];    var numbers = [3,4,5,6];    //工具方法    var util = {      /**       * @desc 图片加载成功的话就执行回调函数       * @param 图片地址 || 图片的DataUrl数据;       */      loadImg : function(e, fn) {        var img = new Image;        if( typeof e !== "string" ) {          img.src = ( e.srcElement || e.target ).result;        }else{          img.src = e;        };        img.onload = function() {//document.body.appendChild( canvas );          //document.getElementById("content").appendChild( canvas );          fn&&fn();        };      }    };

 

 

  代码是基于面向对象(oop), 包含了两个类 :ClipImage 类, Block 类: 

  ClipImage类

    /**     * @desc 把图片通过canvas切成一块块;     */    function ClipImage(canvas , number) {    };    $.extend(ClipImage.prototype, {      /**      * @desc 根据关卡把图片canvas切成块canvas      * 然后渲染到DOM;      * */      clip : function () {      },      /**       * @param 把canvas块混排, 打乱排序;       * */      //使用底线库的方法shuffle打乱排序;      random : function( ) {      },      /**       * @desc 把canvas渲染到DOM;       * */      renderToDom : function () {      },      updataDom : function(cav, obj) {        this.updataMap();        $(cav).animate({top:obj.y*this.avH,left:obj.x*this.avW});      },      updataMap : function () {      },      testSuccess : function () {      }    });

 

 

  Block类

    /**     * @desc 对每一个canvas进行包装;     * @param canvas     * @param left     * @param top     * @param avW     * @param avH     * @constructor Block     */    var Block = function(canvas, left, top,avW, avH) {    };    $.extend(Block.prototype, {      /**       * @desc 对每一个canvas进行定位, 然后添加到界面中;       * */      init : function () {      },      /**       * @desc 对每一个canvas进行定位       * */      setPosition : function() {      },      /**       * @desc 向上移动会执行的函数 ,通过判断maps下有没有对应的key值判断, 界面中的固定位置是否被占用;       * */      upF : function(maps,numbers,cb) {        };      },      /**       * @desc 同上       * */      rightF : function(maps, numbers, cb) {      },      /**       * @desc 同上       * */      downF : function (maps,numbers,cb) {      },      /**       * @desc 同上       * */      leftF : function(maps,numbers,cb) {      }    });

 

  为了考虑移动端,我们使用了zepto封装的swipe系列事件, 默认并没有这个模块, 我们要通过script标签引用进来, github的地址为 https://github.com/madrobby/zepto/blob/master/src/touch.js#files:

      $(document).swipeLeft(function() {        run(clipImage,"leftF")      }).swipeUp(function() {        run(clipImage,"upF")      }).swipeRight(function() {        run(clipImage,"rightF")      }).swipeDown(function() {        run(clipImage,"downF")      });

 

  虽然是一个小游戏,都是要考虑的东西真的不少,包括动画效果, 是否可以移动, 更改数据模型, 是否成功进入下一个关卡等, 包含挺多的判断;

 

  全部代码, 提供思路, 代码可以作为参考:

<!DOCTYPE html><html><head lang="en">  <meta charset="UTF-8">  <title></title>  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">  <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"/>  <script src="http://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>  <script src="http://cdn.bootcss.com/underscore.js/1.8.3/underscore.js"></script>  <style>    body{      margin:0;    }    #content{      position: relative;      margin:40px auto;    }    canvas{      border:1px solid #f0f0f0;      box-shadow: 2px 2px 2px #eee;    }  </style></head><body><input type="file" name="file" id="file"/><div >  <div >    <div >      <div role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" >        <span class>当前是第<span id="now">1</span>关,共4关</span>      </div>    </div>  </div>  <div >    <div id="content" >    </div>  </div></div><script>  (function(fn) {    fn($);  })(function($) {    //这个canvas是缓存图片用的;    var canvas = document.createElement("canvas");    var minScreenWidth = Math.min( document.documentElement.clientWidth/ 2, document.documentElement.clientHeight/2 );    canvas.width = minScreenWidth;    canvas.height = minScreenWidth;    document.getElementById("content").style.width = minScreenWidth + "px";    //保存了所有的block;    var blocks = [];    //工具方法    var util = {      /**       * @desc 图片加载成功的话就执行回调函数       * @param 图片地址 || 图片的DataUrl数据;       */      loadImg : function(e, fn) {        var img = new Image;        if( typeof e !== "string" ) {          img.src = ( e.srcElement || e.target ).result;        }else{          img.src = e;        };        img.onload = function() {          //canvas.width = img.width;          //canvas.height = img.height;          canvas.getContext("2d").drawImage( img, 0, 0 ,canvas.width, canvas.height);          //document.body.appendChild( canvas );          //document.getElementById("content").appendChild( canvas );          fn&&fn();        };      }    };    //绑定事件;    function bindEvents () {      var file = $("#file");      file.bind("change", function(ev) {        var reader = new FileReader;        reader.onload = function(e) {          util.loadImg(e, function() {            window.clipImage = new ClipImage(canvas, numbers[window.lev]);            window.clipImage.random();            Controller( window.clipImage, numbers[window.lev]);          });        };        reader.readAsDataURL(this.files[0]);      });    };    //游戏关卡的图片和游戏每一个关卡要切成的图片快个数    var levels = ["http://sqqihao.github.io/games/jigsaw/lake.jpg","http://sqqihao.github.io/games/jigsaw/cat.jpg","http://sqqihao.github.io/games/jigsaw/follower.jpg","http://sqqihao.github.io/games/jigsaw/view.jpg"];    var numbers = [3,4,5,6];    /**     * @desc 把图片通过canvas切成一块块;     */    function ClipImage(canvas , number) {      //blocks是一个二维数组,保存的是所有的canvas方块;      this.blocks = [];      //instances是一维数组,保存的是实例化的数组;      this.instances = [];      this.maps = {};      this.canvas = canvas;      this.context = this.canvas.getContext("2d");      this.number = number;      this.clip();    };    $.extend(ClipImage.prototype, {      /**      * @desc 根据关卡把图片canvas切成块canvas      * 然后渲染到DOM;      * */      clip : function () {        var avW = this.avW = this.canvas.width/this.number;        var avH = this.avH = this.canvas.height/this.number;        for(var i=0; i< this.number; i++ ) {          for(var j=0; j<this.number; j++ ) {            this.blocks[i] = this.blocks[i] || [];            var canvas = document.createElement("canvas");            canvas.width = avW;            canvas.height = avH;            canvas.x = j;            canvas.y = i;            canvas.map = i+"_"+j;            canvas.correctMap = i+"_"+j;            var imageData = this.context.getImageData(j*avW, i*avH, avW, avH);            canvas.getContext("2d").putImageData( imageData, 0, 0 );            if( i === j && j=== (this.number-1) )break;            // 把canvas放到二维数组blocks中;            this.blocks[i][j] = canvas;          };        };        this.renderToDom();      },      /**       * @param 把canvas块混排, 打乱排序;       * */      random : function( ) {        var len = this.instances.length;        while(len--) {          $(this.instances[len].canvas).remove();        };        //使用底线库的方法shuffle打乱排序;        this.blocks = _.shuffle(this.blocks);        for(var i=0 ;i <this.blocks.length; i++) {          this.blocks[i] = _.shuffle(this.blocks[i]);        }        this.renderToDom();      },      /**       * @desc 把canvas渲染到DOM;       * */      renderToDom : function () {        document.getElementById("content").innerHTML = "";        this.maps = {};        this.doms = [];        this.instances = [];        for(var i=0; i<this.blocks.length; i++ ) {          for(var j=0; j<this.blocks[i].length; j++) {            var instance = new Block( this.blocks[i][j], j, i ,this.avW, this.avH);            //把实例化的数据保存到instances            this.instances.push( instance );            this.maps[i+"_"+j] = true;          };        };      },      updataDom : function(cav, obj) {        this.updataMap();        $(cav).animate({top:obj.y*this.avH,left:obj.x*this.avW});      },      updataMap : function () {        this.maps = {};        var len = this.instances.length;        while(len--) {          this.maps[this.instances[len].canvas.y + "_" + this.instances[len].canvas.x] = true;          this.instances[len].canvas.map = this.instances[len].canvas.y + "_" + this.instances[len].canvas.x;        };        /*        for(var i=0; i<this.blocks.length; i++ ) {          for (var j = 0; j < this.blocks[i].length; j++) {            this.maps[this.blocks[i][j].y + "_" + this.blocks[i][j].x] = true;          }        }*/      },      testSuccess : function () {        var len = this.instances.length;        while(len--) {          //只要有一个不等就无法成功;          if(this.instances[len].canvas.correctMap !== this.instances[len].canvas.map) {            return ;          };        };        console.log("成功");        if( ++window.lev >=4 ) {          alert("已经通关");          return ;        } ;        $("#now").html( window.lev + 1 );        $(".progress-bar").width( (window.lev+ 1) * 25 + "%" );        init(window.lev);      }    });    /**     * @desc 对每一个canvas进行包装;     * @param canvas     * @param left     * @param top     * @param avW     * @param avH     * @constructor Block     */    var Block = function(canvas, left, top,avW, avH) {      this.canvas = canvas;      this.left = left;      this.top = top;      this.avW = avW;      this.avH = avH;      this.init();    };    $.extend(Block.prototype, {      /**       * @desc 对每一个canvas进行定位, 然后添加到界面中;       * */      init : function () {        this.canvas.style.position = "absolute";        this.canvas.style.left = this.avW*this.left +"px";        this.canvas.style.top = this.avH*this.top +"px";        this.canvas.x = this.left;        this.canvas.y = this.top;        document.getElementById("content").appendChild( this.canvas );      },      /**       * @desc 对每一个canvas进行定位       * */      setPosition : function() {        this.canvas.style.left = this.avW*this.canvas.x +"px";        this.canvas.style.top = this.avH*this.canvas.y +"px";      },      /**       * @desc 向上移动会执行的函数 ,通过判断maps下有没有对应的key值判断, 界面中的固定位置是否被占用;       * */      upF : function(maps,numbers,cb) {        //如果目标有        var temp = (this.canvas.y>0 ? (this.canvas.y-1) : this.canvas.y);        var targetXY = temp+"_"+this.canvas.x;        if( !maps[targetXY] ) {          this.canvas.y = temp;          this.canvas.map = targetXY;          //alert("可以走")          cb(this.canvas, {            x : this.canvas.x,            y : this.canvas.y          });          return true;        };      },      /**       * @desc 同上       * */      rightF : function(maps, numbers, cb) {        var temp = ((this.canvas.x+1>numbers-1) ? this.canvas.x : this.canvas.x+1);        var targetXY = this.canvas.y+"_"+temp;        if( !maps[targetXY] ) {          this.canvas.x = temp;          this.canvas.map = targetXY;          //alert("可以走")          cb(this.canvas, {            x : this.canvas.x,            y : this.canvas.y          });          return true;        };      },      /**       * @desc 同上       * */      downF : function (maps,numbers,cb) {        var temp = ((this.canvas.y+1>numbers-1) ? this.canvas.y : this.canvas.y+1);        var targetXY = temp+"_"+this.canvas.x        if( !maps[targetXY] ) {          this.canvas.y = temp;          this.canvas.map = targetXY;          cb(this.canvas, {            x : this.canvas.x,            y : this.canvas.y          });          //alert("可以走");          return true;        };      },      /**       * @desc 同上       * */      leftF : function(maps,numbers,cb) {        var temp = ( (this.canvas.x-1)>=0 ? this.canvas.x-1 : this.canvas.x );        var targetXY = this.canvas.y+"_"+temp;        if( !maps[targetXY] ) {          this.canvas.x = temp;          this.canvas.map = targetXY;          //alert("可以走")          cb(this.canvas, {            x : this.canvas.x,            y : this.canvas.y          });          return true;        };      }    });    /**     * @desc 主要控制器;     *     * */    function Controller( clipImage, number) {      var run = function( clipImage, name ) {        //window.clipImage.doms ,window.clipImage.maps, numbers[level], window.clipImage.updataDom.bind(window.clipImage)        for(var i=0; i<clipImage.instances.length; i++ ) {          var instance = clipImage.instances[i];          if( instance[name].bind(instance)(clipImage.maps, number, clipImage.updataDom.bind(clipImage)) ) {            clipImage.testSuccess();            return          };        };      }      $(window).unbind("keydown");      $(window).bind("keydown", function(ev) {        var name;        switch(ev.keyCode) {          case 37 :            name = "leftF";            break;          case 38 :            name = "upF";            break;          case 39 :            name = "rightF";            break;          case 40 :            name = "downF";            break;          default :            ev.preventDefault();            return false        };        run( clipImage, name );        ev.preventDefault();      });      $(document).swipeLeft(function() {        run(clipImage,"leftF")      }).swipeUp(function() {        run(clipImage,"upF")      }).swipeRight(function() {        run(clipImage,"rightF")      }).swipeDown(function() {        run(clipImage,"downF")      });    };    function init(level) {      util.loadImg( levels[level] ,function() {        window.clipImage = new ClipImage(canvas, numbers[level]);        window.clipImage.random();        Controller( window.clipImage, numbers[level] || 3);      });    };    $(function() {      window.lev = 0;      init(lev);      bindEvents();    });  });</script></body></html>

View Code

 

        如果有bug直接评论, 我会修正, 提git的issue也行,

  DEMO地址查看:打开

 

作者: NONO
出处:http://www.cnblogs.com/diligenceday/
QQ:287101329 




去云南旅游必去景点去云南旅游要多少钱去云南旅游最佳路线去云南旅游最佳时间云南旅游线路报价上林霞客古渡景区小孩怎么收费?霞客古渡儿童票价格? 黄腾峡山水乐园好玩吗?清远黄腾峡山水乐园怎么样? 霞客古渡景区电话?上林霞客古渡团购优惠多少? 清远黄腾峡山水乐园几点开门?黄腾峡山水乐园开门时间? 惠州哪里有大片的薰衣草?惠州赏薰衣草的好地方? 惠州三月赏花的好地方?广东惠州赏花攻略? 去东瀛风情的樱花之都邂逅浪漫樱花 永记生态园薰衣草开了吗?惠州永记生态园薰衣草几月份最好看? 重庆新开这些航线 冬季出游更方便了 带数字的重庆镇街乡地名 万圣节一起开启津城寻鬼之旅——X街区-综合娱乐体验馆 第九届深圳(大梅沙)国际风筝节是什么时候?有什么好玩的? 马尔代夫椰子岛有哪些住宿的地方? 巴厘岛好玩吗? 泰国曼谷机场有ATM机吗?泰国银行营业到几点? 马尔代夫伊露岛好玩吗? 1210YC125KAT2A Datasheet 1210YC125KAT2A Datasheet AQ147A120FAJME Datasheet AQ147A120FAJME Datasheet 12061C392KAT2A Datasheet 12061C392KAT2A Datasheet 四平到港澳五日游 四平到港澳五日游 四平到港澳五日游 四平跟团香港两日游价格 四平跟团香港两日游价格 四平跟团香港两日游价格 内蒙古香港两日游 内蒙古香港两日游 内蒙古香港两日游