成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

HTML 簡單拼圖游戲

vslam / 1348人閱讀

摘要:基本需求有一個(gè)固定區(qū)域,被拆分成個(gè)同等大小的碎片,拿走其中一塊,靠近缺口的塊可以向缺口方向移動(dòng)當(dāng)拼出原來的圖樣視為完成。左右移動(dòng)很簡單,序號(hào)大的序號(hào)小的即可。

先不廢話,請(qǐng)看演示。

公司要搞這么個(gè)微信活動(dòng),可現(xiàn)在沒有前端開發(fā),沒辦法,身為打雜總監(jiān)只好臨時(shí)頂下這個(gè)空缺了。先找了一些 JS 代碼,試用了下都不太理想,好一點(diǎn)的寫的又太復(fù)雜,改起來有難度,干脆擼起袖子自己干。

基本需求

有一個(gè)固定區(qū)域,被拆分成 c*r 個(gè)同等大小的碎片,拿走其中一塊,靠近缺口的塊可以向缺口方向移動(dòng);當(dāng)拼出原來的圖樣視為完成。

依照此需求,需要經(jīng)歷 加載圖片-》拆分圖片-》隨機(jī)打散-》移動(dòng)碎片-》判定完成 這些步驟。為了更有可玩性,能自行選擇自己的圖片就更妙了。

下面就重點(diǎn)說明下各個(gè)步驟,為編寫方便,引入 jQuery 作為輔助庫。

加載圖片

首先當(dāng)然是載入圖片,計(jì)算寬高,對(duì)比拼圖區(qū)域的尺寸進(jìn)行縮放,如果比例不同,還得“裁剪”掉多余的部分。

/**
 * 加載圖片
 * cal 的回調(diào)參數(shù)為:
 *  ox 橫向偏移
 *  oy 縱向偏移
 *  this 指向載入的圖片的 jQuery 對(duì)象
 * @param {String} src 圖片路徑
 * @param {int} w 額定寬
 * @param {int} h 額定高
 * @param {Fucntion} cal 加載完成后的回調(diào)方法
 */
function loadr(src, w, h, cal) {
    var img  =  new Image();
    img.onload = function() {
        var xw = img.width ;
        var xh = img.height;
        var zw = xh * w / h;
        if (zw > xw) {
            // 寬度優(yōu)先
            img.width   = w;
            img.height  = xh * w / xw;
            xh = (h - img.height) / 2;
            xw = 0;
        } else {
            // 高度優(yōu)先
            img.height  = h;
            img.width   = xw * h / xh;
            xw = (w - img.width ) / 2;
            xh = 0;
        }

        cal.call(img, xw, xh);
    };
    img.src = src ;
}

以上的“裁剪”僅僅是計(jì)算出偏移,然后將其傳遞給加載就緒的回調(diào)函數(shù)。

拆分圖片

圖有了,已縮放,現(xiàn)在需要“拆分”成碎片。這里自然不是真的切割了,而是將圖片 clone 出 c*r 片,然后利用負(fù)的坐標(biāo)定位,其實(shí)質(zhì)是用一個(gè)塊遮蓋了“切除”的部分,僅顯示需要的碎片部分。

/**
 * 拆分圖片
 * @param {jQuery} that 容器對(duì)象
 * @param {int} cols 行
 * @param {int} rows 列
 * @param {int} ew 板塊寬度
 * @param {int} eh 板塊高度
 * @param {int} ox 圖片橫向偏移
 * @param {int} oy 圖片縱向偏移
 * @param {Image} im 圖片對(duì)象
 */
function split(that, cols, rows, ew, eh, ox, oy, im) {
    that.empty();

    for(var j = 0 ; j < rows; j ++) {
        for(var i = 0 ; i < cols; i ++) {
            var k = i + j * rows;

            var pic = $("
"); pic.attr("id", "pt-pic-"+k); pic.data("idx", k); pic.appendTo(that); pic.css ({ "position": "relative", "overflow": "hidden", "border" : "0", "width" : ew + "px", "height" : eh + "px" }); var img = $(im.cloneNode()); img.appendTo(pic); img.css ({ "position": "absolute", "z-index" : "88", "border" : "0", "left" : (0 - i * ew + ox) + "px", "top" : (0 - j * eh + oy) + "px" }); // 因邊框可能影響寬高計(jì)算, 故邊框多帶帶用一個(gè)塊來放 var bor = $("
"); bor.appendTo(pic); bor.css ({ "position": "absolute", "z-index" : "99", "width" : "100%", "height" : "100%" }); // 由于樣式寬高并不含邊框, 故再次計(jì)算尺寸的偏移量 bor.css ({ "width" : (2 * bor.width () - bor.outerWidth ()) + "px", "height" : (2 * bor.height() - bor.outerHeight()) + "px" }); } } }

稍微注意,為方便人眼分辨碎片,最好給碎片加個(gè)邊框,但加邊框必然影響坐標(biāo)的計(jì)算,故在圖片上再覆蓋一層,邊框設(shè)在他上面,就算加個(gè)撕裂效果的透明圖做邊框都沒問題了。這樣碎片內(nèi)圖片的偏移坐標(biāo)的計(jì)算就少了些麻煩了。

隨機(jī)打散

這游戲當(dāng)然是跟電腦玩了,總不能自己打散自己玩吧?但這個(gè)打散不能給每個(gè)圖片一個(gè)隨機(jī)位置,那很可能你永遠(yuǎn)也拼不回去了。小時(shí)拿那種拼圖游戲板整人就干過這種事,故意摳下來把頭和腳交換再打散,然后跟其他小朋友打賭。所以程序也得守規(guī)矩一塊一塊的移動(dòng)。

/**
 * 打散圖片
 * @param {jQuery} that 容器對(duì)象
 * @param {int} cols 列
 * @param {int} rows 行
 * @param {int} rand 打散步數(shù)
 */
function upset(that, cols, rows, rand) {
    var v ;
    var r = Math.floor(Math.random()  *  cols  *  rows);
    var hole = that.children().eq(r).addClass("pt-pix");
    var part ;
    var step = [];
    var dbug = [];
    for(var  i = 0, j = rand; i < j; i ++) {
        var  x = cols - 1;
        var  y = rows - 1;
        var  z = cols;
        var rx = r % cols;
        var ry = Math.floor(r / cols);
        var rv = [];

        if (rx > 0 && rx < x) {
            rv.push(r - 1, r + 1); // 可左右移動(dòng)
        } else
        if (rx > 0) {
            rv.push(r - 1); // 可向左移動(dòng)
        } else
        {
            rv.push(r + 1); // 可向右移動(dòng)
        }
        if (ry > 0 && ry < y) {
            rv.push(r - z, r + z); // 可上下移動(dòng)
        } else
        if (ry > 0) {
            rv.push(r - z); // 可向上移動(dòng)
        } else
        {
            rv.push(r + z); // 可向下移動(dòng)
        }

        // 排除來源位置
        if (step.length > 0) {
            v = step[step.length - 1];
            v = $.inArray(v, rv);
            if (v > -1) {
                rv.splice(v, 1 );
            }
        }
        // 排除回旋位置
        if (step.length > 2 && rv.length > 1) {
            v = step[step.length - 3];
            v = $.inArray(v, rv);
            if (v > -1) {
                rv.splice(v, 1 );
            }
        }

        // 隨機(jī)方向
        r = rv[Math.floor(Math.random()* rv.length)];
        v = hole.index();
            step.push(v);

        // 交換位置
        part  = that.children().eq( r );
        if (r < v) {
            part.insertBefore(hole);
            hole.insertBefore(that.children().eq(r));
        } else {
            hole.insertBefore(part);
            part.insertBefore(that.children().eq(v));
        }

        // 調(diào)試步驟
        if (r == v + 1) {
            dbug.push("左");
        } else
        if (r == v - 1) {
            dbug.push("右");
        } else
        if (r > v) {
            dbug.push("上");
        } else
        if (r < v) {
            dbug.push("下");
        }
    }

    // 攻略
    dbug = dbug.reverse().join(" "); alert(dbug);
    console.log( "攻略: "+dbug+"
此非最優(yōu)解, 僅為隨機(jī)打散時(shí)的逆向步驟, 上下左右為相對(duì)缺口的板塊, 祝您玩的開心!" );
}

把打散的步驟記錄下來,然后反轉(zhuǎn)數(shù)組,就是攻略啦。

不過隨機(jī)時(shí)需要避免往回走,否則出現(xiàn) 左->右->左 這類情況就不好玩了;還得避免其他循環(huán),如 上->右->下->左 這樣的,這會(huì)回到原點(diǎn),等于什么也沒干;但更大的循環(huán)沒想好怎么處理,暫時(shí)不去糾結(jié)了。

移動(dòng)判定

移動(dòng)碎片到缺口,也就是交換碎片與缺口的位置。左右移動(dòng)很簡單,序號(hào)大的 insertBefore 序號(hào)小的即可。上下移動(dòng)有個(gè)小坑,開始自己沒注意,我原本想不管橫向還是縱向,沒有兩次 insertBefore 搞不定的,但是如果 3 和 7 交換位置(3x3, 0~8),3 移動(dòng)到 7 前,7 再移動(dòng)到 3 前,此時(shí)原來的 3 變成了 6。的確,沒有什么是不能兩次 insertBefore 解決的,但還得考慮讓序號(hào)大的先動(dòng)。

/**
 * 移動(dòng)板塊
 * @param {jQuery} that 容器對(duì)象
 * @param {int} cols 列數(shù)
 * @param {int} rows 行數(shù)
 * @param {jQuery} hole 缺口對(duì)象
 * @param {jQuery} part 板塊對(duì)象
 */
function mover(that, cols, rows, hole, part) {
    var move = false ;
    var i  = part.index();
    var j  = hole.index();
    var ix = i % cols;
    var jx = j % cols;
    var iy = Math.floor(i / cols);
    var jy = Math.floor(j / cols);

    if (iy == jy) { // 在同一行
        move  = ix == jx + 1  // 可向左邊移動(dòng)
             || ix == jx - 1; // 可向右邊移動(dòng)
    } else
    if (ix == jx) { // 在同一列
        move  = iy == jy + 1  // 可向上移動(dòng)
             || iy == jy - 1; // 可向下移動(dòng)
    }

    // 互換位置
    if (move) {
        if (i  <  j ) {
            part.insertBefore(hole);
            hole.insertBefore(that.children().eq(i));
        } else {
            hole.insertBefore(part);
            part.insertBefore(that.children().eq(j));
        }
    }

    // 判斷是否拼圖完成
    move = true;
    for (i = 0, j = cols * rows; i < j; i ++) {
        if (that.children().eq(i).data("idx") != i) {
            move = false;
        }
    }

    return  move;
}

判斷是否完成就來個(gè)笨辦法吧,依次遍歷所有碎片,只要有一個(gè)沒對(duì)上序號(hào)就是還沒成功。

未處理滑動(dòng)事件,以后閑了再加吧。

整合游戲程序

上面分散的幾個(gè)函數(shù)用起來還是不太方便,整合成一個(gè) jQuery 插件。

/**
 * 拼圖游戲
 * @param {String} src 圖片路徑
 * @param {int} cols 列數(shù)
 * @param {int} rows 行數(shù)
 * @param {int} rand 打散步數(shù)
 */
$.fn.hsPintu = function(src, cols, rows, rand) {
    var that = $(this);
    var srz  = that.data("src");
    var img  = that.data("img");

    var aw = that.width ();
    var ah = that.height();
    var ew = aw / rows;
    var eh = ah / cols;

    // 狀態(tài): 0 進(jìn)行中, 1 成功, 2 結(jié)束
    that.data("hsPintuStatus", 2);
    that.data("cols", cols);
    that.data("rows", rows);

    /**
     * img 存在且 src 沒變化
     * 則不需要再次加載圖片
     * 直接取出存儲(chǔ)好的數(shù)據(jù)
     */
    if (img && srz === src) {
        var ox = that.data("pos_x");
        var oy = that.data("pos_y");
        console.log("Note: 圖片無變化");

        split(that, cols, rows, ew, eh, ox, oy, img );

        // 未給 rand 則僅拆分而不打散
        if (rand === undefined) return;

        upset(that, cols, rows, rand);
        that.data("hsPintuStatus", 0);
        that.trigger("hsPintuLaunch");
    } else
    loadr(src, aw, ah, function(ox, oy) {
        that.data("src", src );
        that.data("img", this);
        that.data("pos_x", ox);
        that.data("pos_y", oy);
        console.log("Note: 載入新圖片");

        split(that, cols, rows, ew, eh, ox, oy, this);

        // 未給 rand 則僅拆分而不打散
        if (rand === undefined) return;

        upset(that, cols, rows, rand);
        that.data("hsPintuStatus", 0);
        that.trigger("hsPintuLaunch");
    });

    // 已經(jīng)初始化過就不要再綁定事件了
    if (! that.data("hsPintuInited")) {
        that.data("hsPintuInited", 1);

        that.on("click", ".pt-pic:not(.pt-pix)", function() {
            if (that.data("hsPintuStatus") === 0) {
                var cols =that.data("cols");
                var rows =that.data("rows");
                var hole =that.children(".pt-pix");
                if (mover(that, cols, rows, hole, $(this))) {
                    that.data("hsPintuStatus", 1);
                    that.trigger("hsPintuFinish");
                }
            }
        });
    }

    return  this;
};

$("#pt-box").hsPintu(圖片URL, 列數(shù), 行數(shù)[, 隨機(jī)步數(shù)]); 即可初始化拼圖游戲了, 拼圖區(qū)域需要固定寬高;隨機(jī)步數(shù)參數(shù)不提供時(shí),僅拆解不打散。

圖片沒變化時(shí)沒必要重新加載,避免下時(shí)間損耗。當(dāng)然了,更好的辦法是再判斷行、列和區(qū)域尺寸,沒變化則直接排列好碎片。懶得寫了,先這樣吧。

選擇任意圖片

上面都是固定的圖片,參與感不好,讓用戶自行“上傳”圖片豈不更有意思。其實(shí)不必真的上傳到服務(wù)器,既然“縮放”、“裁剪”上面都有了,直接加載本地圖片不就好了嘛。

/**
 * 預(yù)載文件
 * @param {Function} cal 回調(diào)函數(shù)
 * @returns {jQuery} 當(dāng)前文件節(jié)點(diǎn)
 */
$.fn.hsFileLoad = function(cal) {
    this.each(function() {
        var that = this;
        if (window.FileReader) {
            var fr = new FileReader( );
            fr.onloadend = function(e) {
                cal.call(that, e.target.result);
            };  cal.call(that);
            $.each( this.files, function(i, fo) {
                fr.readAsDataURL( fo );
            });
        } else
        if (this.getAsDataURL) {
            cal.call(that, that.getAsDataURL());
        } else {
            cal.call(that, that.value);
        }
    });
    return this;
};

這段代碼也能從我的開源項(xiàng)目內(nèi)找到 預(yù)載文件 方法,此工具包還有些其他的文件上傳預(yù)覽類的方法,這是我對(duì) bootstrap-fileinput 沒有圖片裁剪功能(與最終服務(wù)端處理后的結(jié)果一致)而“一氣之下”自己寫的一點(diǎn)零散代碼。

完整的代碼及演示可在 這里 看到,有朋友說看不到圖,但圖片我用的百度圖片搜索的縮略圖,不清楚怎么回事,看不到可以自己從本地選擇圖片。只是那個(gè)“加載”(Image.onload)和“切片”(Image.cloneNode)比較耗時(shí),比較大的圖片請(qǐng)耐心等等。

當(dāng)然了,也可以光顧我們的活動(dòng)頁玩一把 拼圖抽獎(jiǎng)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79755.html

相關(guān)文章

  • 打造專屬自己的html5拼圖游戲

    摘要:最近公司剛好有個(gè)活動(dòng)是要做一版的拼圖小游戲,于是自己心血來潮,自己先實(shí)現(xiàn)了一把,也算是嘗嘗鮮了。下面就把大體的思路介紹一下,希望大家都可以做出一款屬于自己的拼圖小游戲,必須是更炫酷,更好玩來吧,大家一起加油。。。 最近公司剛好有個(gè)活動(dòng)是要做一版 html5的拼圖小游戲,于是自己心血來潮,自己先實(shí)現(xiàn)了一把,也算是嘗嘗鮮了。下面就把大體的思路介紹一下,希望大家都可以做出一款屬于自己的拼圖小...

    codeKK 評(píng)論0 收藏0
  • 打造專屬自己的html5拼圖游戲

    摘要:最近公司剛好有個(gè)活動(dòng)是要做一版的拼圖小游戲,于是自己心血來潮,自己先實(shí)現(xiàn)了一把,也算是嘗嘗鮮了。下面就把大體的思路介紹一下,希望大家都可以做出一款屬于自己的拼圖小游戲,必須是更炫酷,更好玩來吧,大家一起加油。。。 最近公司剛好有個(gè)活動(dòng)是要做一版 html5的拼圖小游戲,于是自己心血來潮,自己先實(shí)現(xiàn)了一把,也算是嘗嘗鮮了。下面就把大體的思路介紹一下,希望大家都可以做出一款屬于自己的拼圖小...

    JowayYoung 評(píng)論0 收藏0
  • 打造專屬自己的html5拼圖游戲

    摘要:最近公司剛好有個(gè)活動(dòng)是要做一版的拼圖小游戲,于是自己心血來潮,自己先實(shí)現(xiàn)了一把,也算是嘗嘗鮮了。下面就把大體的思路介紹一下,希望大家都可以做出一款屬于自己的拼圖小游戲,必須是更炫酷,更好玩來吧,大家一起加油。。。 最近公司剛好有個(gè)活動(dòng)是要做一版 html5的拼圖小游戲,于是自己心血來潮,自己先實(shí)現(xiàn)了一把,也算是嘗嘗鮮了。下面就把大體的思路介紹一下,希望大家都可以做出一款屬于自己的拼圖小...

    趙春朋 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<