JavaScript写一个拼图游戏

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

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

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

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

  

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

    JavaScript写一个拼图游戏

  

  因为要考虑到移动端的效果, 所以主界面图片是根据屏幕适配, 拼图大图的大小是屏幕宽度和屏幕高度之间最小值的一半, 都是为了不出现滚动条。 比如:用户的手机是横屏模式, 这个横屏的宽度是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")

            });

 

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

 

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

JavaScript写一个拼图游戏
<!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 class="container">

    <div class="row">

        <div class="progress">

            <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 25%">

                <span class>当前是第<span id="now">1</span>关,共4关</span>

            </div>

        </div>

    </div>

    <div class="row">

        <div id="content" class="clearfix ">



        </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 

你可能感兴趣的:(JavaScript)