用JS寫一個(gè)拼圖游戲

思路:

  1. 上傳一張自己的本地的圖片
  2. 將圖片分割成若干行若干列
+ 給每一個(gè)小格子,添加上自己的索引
+ 把索引存入一個(gè)數(shù)組中,方便最后判斷是否完成
  1. 分割的每一個(gè)小格子上都顯示完整圖片對(duì)應(yīng)的一部分
  2. 點(diǎn)擊開始游戲按鈕,打亂圖片的位置
+ 創(chuàng)建一個(gè)新數(shù)組,將原數(shù)組打亂之后存到新數(shù)組中
+ 讓每一個(gè)小格子根據(jù)打亂后的數(shù)組進(jìn)行定位
  1. 點(diǎn)擊兩個(gè)不同的小格,讓其交換位置。
+ 小格子位置交換完成后,需要將打亂的數(shù)組對(duì)應(yīng)的兩個(gè)值也交換位置。
  1. 判斷交換后的亂序數(shù)組和原來的數(shù)組是否相等,如果相等即表示游戲完成。

難點(diǎn):

  1. 如何上傳本地圖片讓其顯示的
  2. 如何將一張圖片切割成多個(gè)小格子
  3. 如果根據(jù)亂序的數(shù)組來計(jì)算當(dāng)前的行數(shù)和列數(shù)

解決辦法:

  1. 針對(duì)于如何上傳一張本體圖片,看之前的一篇關(guān)于瀏覽器如何在頁面上顯示準(zhǔn)備上傳的圖片的問題,下面是傳送門。
+ <a target = "_blank" href="http://www.itdecent.cn/p/9e4a1ee03089">關(guān)于瀏覽器如何在頁面上顯示準(zhǔn)備上傳的圖片的問題</a>
  1. 針對(duì)如何切割一張圖片的問題,事實(shí)上也并不能說是切割,且聽我細(xì)細(xì)道來。
+ 首先我們利用兩個(gè)for循環(huán),外層代表行數(shù),里面代表列數(shù),然后動(dòng)態(tài)創(chuàng)建div來表示每一個(gè)小方格。
+ 然后我們分隔好了小方格,我們也知道小方格的寬和高,并且知道每行每列各有多少個(gè)小方格。
+ 之后我們給每一個(gè)小方格都設(shè)置一個(gè)`background-image`屬性,讓每一個(gè)方格都有一個(gè)完整的圖。所以說這里并不是一張背景圖,而是有多少個(gè)小方格就有多少張圖片。
+ 再之后根據(jù)行數(shù)和列數(shù)的數(shù)量來設(shè)置`background-size`,比如只有一個(gè)格子,`background-size`的值就是 100% ,同理,兩個(gè)值就是200%
+ 最后根據(jù)當(dāng)前第幾行,第幾列,每一個(gè)小格子的寬度和高度來計(jì)算出偏移量,即left值和top值。再利用`background-position`來進(jìn)行定位即可實(shí)現(xiàn)。
  1. 針對(duì)根據(jù)亂序數(shù)組來計(jì)算當(dāng)前的行數(shù)和列數(shù)。這里其實(shí)也并不難,只是我個(gè)人卡在這里卡了一會(huì)兒。。。。
+ 首先我們的亂序數(shù)組是根據(jù)原來的數(shù)組打亂順序而來,所以里面的數(shù)值除了順序應(yīng)該就是一樣的。
+ 當(dāng)我們點(diǎn)擊圖片的時(shí)候能夠獲取到圖片所在的div的索引。
+ 因?yàn)樵瓟?shù)組是按照0,1,2,3,4.....這樣排列的,所以這個(gè)索引在亂序數(shù)組中對(duì)應(yīng)的值就是打亂后的圖片是從0開始數(shù)的第多少張圖片。
+ 知道了這張圖片此時(shí)是屬于第幾張,我們就能根據(jù)這個(gè)值來計(jì)算出這張圖片此時(shí)對(duì)應(yīng)的行數(shù)和列數(shù)。   
  - 當(dāng)前是第幾張圖片 / 一行多少列 然后取整 就是當(dāng)前屬于第幾行
  - 當(dāng)前是第幾張圖片 % 一行多少列 然后取余 就是當(dāng)前屬于第幾列

這里我用一張動(dòng)圖來表示如果切割圖片的

如何把一張圖切割成多塊.gif

注意點(diǎn):

  1. 打亂索引 的時(shí)候要注意,由于打亂的數(shù)組可能跟原數(shù)組還是一樣,所以需要控制一下。
  2. 上傳的本地的圖片不要太大,不然讀取的速度那還真有點(diǎn)慢。。。
  3. 上傳的圖片需要是個(gè)正方形的,實(shí)在沒有正方向的話,高度大于寬度的圖片也行,無非是下半部分被干掉了。。
  4. 因?yàn)閭€(gè)人比較懶。很多地方?jīng)]有寫兼容,所以打開小游戲請(qǐng)使用chrome瀏覽器

我們來看看關(guān)鍵代碼

  1. HTML部分
  <!-- 獲取圖片 -->
  <input type="file" id="file">
  <!-- 字體圖標(biāo) -->
  <div id="btn">
      <i class="iconfont icon-xiangji2"></i>      
  </div>
  <div id="gameArea">
      <!-- 游戲開始按鈕 -->
      <div id="gameStart">點(diǎn)擊開始</div>
      <!-- 圖片存放的區(qū)域 -->
      <div id="imgArea"></div>
  </div>
  1. CSS部分

css部分真沒啥好說的,有興趣的話大家下載源碼,一看就知道了

  1. JS部分
  • 切割圖片
      // 切割圖片
    imgSplit:function(){
        // 清空?qǐng)D片存放區(qū)域
        this.imgArea.innerHTML = '';
        // 用來存方動(dòng)態(tài)創(chuàng)建的div元素
        var _cell = '';
    
        // 行數(shù)
        for (var i = 0, l =this.leverArr[0]; i<l ;i++) {
            // 列數(shù)
            for (var j = 0,l =this.leverArr[1]; j<l ;j++) {
                // 給每張圖片一個(gè)索引值
                // 索引遞增規(guī)則為  從左到右  從上到下
                this.imgOrigArr.push(i*this.leverArr[0] + j);
                // 創(chuàng)建div
                _cell = document.createElement('div');
                // 給div添加id
                _cell.className = "imgCell";
                // 給每張一個(gè)索引,方便后面點(diǎn)擊的時(shí)候進(jìn)行判斷
                _cell.index = i*this.leverArr[0] + j;
                // 給div添加樣式
                _cell.style.width = this.cellWidth+'px';
                _cell.style.height = this.cellHeight+'px';
                _cell.style.left =  j*this.cellWidth+'px';
                _cell.style.top = i*this.cellHeight + 'px';
                _cell.style.backgroundImage= "url("+this.imgUrl+")";
                // 這里因?yàn)?00%就讓背景圖的大小等于了一個(gè)小格子的大小
                // 而我們只需要原始圖的一部分,并不是想縮小原圖
                // 所以根據(jù)小格子的個(gè)數(shù)來放大圖片
                _cell.style.backgroundSize = this.leverArr[1]+'00%';
                // 移動(dòng)背景圖,行成最后切成的一塊塊的效果
                _cell.style.backgroundPosition = (-j)*this.cellWidth + 'px ' + (-i)*this.cellHeight+'px';
                // 讓背景圖從邊框開始平鋪
                _cell.style.backgroundOrigin = "border-box";
                this.imgArea.appendChild(_cell);
            }
        }
    
        // 獲取小格子的dom元素
        this.imgCells = document.querySelectorAll('.imgCell');
    
        //將選擇圖片的按鈕移動(dòng)到可視區(qū)域外
        this.btnObj.style.left= -this.btnObj.offsetWidth+'px';
        // 將圖片移入可視區(qū)域
        // this.gameAreaObj.style.background = 'url('+this.imgUrl +')';
        this.gameAreaObj.style.left = '50%';
        this.gameAreaObj.style.transform= 'translate(-50%,-50%)';
    
        // 使按鈕綁定事件,點(diǎn)擊按鈕開始整個(gè)游戲
        this.gameStartBtnObj.onclick = this.clickHandle();
    }
    
  • 打亂圖片索引
      // 打亂圖片索引
    randomArr:function(){
        // 清空亂序數(shù)組
        this.imgRandomArr = [];
        // 判斷原來的數(shù)組是否和亂序數(shù)組一樣
        var _flag = true;
        // 遍歷原始索引
        for(var i=0,l=this.imgOrigArr.length;i<l;i++){
            // 獲取從0到數(shù)組長度之間的一個(gè)索引值
            var order = Math.floor(Math.random()*this.imgOrigArr.length);
            // 如果亂序數(shù)組中沒有值就直接添加
            // 否則就在這個(gè)亂序數(shù)組中找對(duì)應(yīng)的隨機(jī)數(shù)的索引,找不到就添加,找到就繼續(xù)隨機(jī)
            if(this.imgRandomArr.length>0){
                while(this.imgRandomArr.indexOf(order) >-1){
                    order = Math.floor(Math.random()*this.imgOrigArr.length);
                }
            }
            this.imgRandomArr.push(order);
        }
    
        // 判斷亂序數(shù)組和原始數(shù)組是否一樣
        if(this.imgRandomArr.length === this.imgOrigArr.length){
            // 遍歷數(shù)組
            for(var i=0,l=this.imgOrigArr.length;i<l;i++){
                if(this.imgRandomArr[i] != this.imgOrigArr[i]){
                    _flag = false;
                    break;
                }else{
                    _flag = true;
                }
            }
        }else{
            _flag = true;
        }
    
        // 返回值為true的話 就代表原始數(shù)組和亂序數(shù)組一致,重新打亂數(shù)組
        if(_flag){
            this.randomArr();
        }
    }
    
  • 交換兩次點(diǎn)擊的圖片位置
      // 交換兩次點(diǎn)擊的圖片位置
    cellExchange:function(from,to){
        // 因?yàn)閳D片此時(shí)的排序是根據(jù) 以圖片的索引值為索引
        // 在亂序的數(shù)組中根據(jù)相對(duì)應(yīng)的索引取出的值作為當(dāng)前圖片的排序位置的
        // 因此根據(jù)from to這兩個(gè)值作為索引,就能在亂序數(shù)組中得到當(dāng)前圖片是第幾張
    
        // 求出from的圖片 是第幾行第幾列
        // 當(dāng)前是第幾張圖片 / 一行多少列 然后取整 就是當(dāng)前屬于第幾行
        var _fromRow = Math.floor(this.imgRandomArr[from] / this.leverArr[1]);
        // 當(dāng)前是第幾張圖片 % 一行多少列 然后取余 就是當(dāng)前屬于第幾列
        var _fromCol = this.imgRandomArr[from] % this.leverArr[1];
        // 求出to的圖片 是第幾張第幾列
        var _toRow = Math.floor(this.imgRandomArr[to] / this.leverArr[1]);
        var _toCol = this.imgRandomArr[to] % this.leverArr[1];
    
        // 移動(dòng)兩張圖片
        this.imgCells[from].style.left = _toCol*this.cellWidth + 'px';
        this.imgCells[from].style.top = _toRow*this.cellHeight + 'px';
    
        this.imgCells[to].style.left = _fromCol*this.cellWidth + 'px';
        this.imgCells[to].style.top = _fromRow*this.cellHeight + 'px';
    
        // 將亂序數(shù)組中的兩個(gè)值交換位置
        // 定義一個(gè)臨時(shí)變量來實(shí)現(xiàn)交換順序
        var _temp = this.imgRandomArr[from];
        this.imgRandomArr[from] = this.imgRandomArr[to];
        this.imgRandomArr[to] = _temp;
    
        //如果亂序數(shù)組和原數(shù)組一致,則表示拼圖已完成
        if(this.imgOrigArr.toString() === this.imgRandomArr.toString()){
            // 調(diào)用成功方法
            this.success();
        }
        
    }
    
源碼保存在了<a target = "_blank" >github</a>上,有愛點(diǎn)擊github自取。
在線地址可以直接測(cè)試使用,<a target = "_blank" >拼圖小游戲</a>←點(diǎn)擊跳轉(zhuǎn)即可

總結(jié):

  1. 最近不知道怎么了,老是不在狀態(tài),腦袋反應(yīng)比較慢,一個(gè)簡(jiǎn)單問題總是要想好久。感覺整個(gè)人都魔怔了。。。。
  2. 本來我是想自動(dòng)獲取圖片寬高來切成行列的,不過行數(shù)列數(shù)不相等的話,好像有點(diǎn)麻煩。而且不是正方形也不好看啊。想了想還是不弄了。。想興趣的朋友自己弄弄吧。。
  3. 順便一提,上面的動(dòng)圖所用到的圖片,畫師是P站的 METO@三日目東ミ24a
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,120評(píng)論 2 17
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象,但只有一個(gè)實(shí)例,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,314評(píng)論 1 10
  • 《ijs》速成開發(fā)手冊(cè)3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 5,640評(píng)論 0 7
  • 以下是常用的代碼收集,學(xué)習(xí)用。轉(zhuǎn)自豪情博客園 1. PC - js 返回指定范圍的隨機(jī)數(shù)(m-n之間)的公式 re...
    自由加咖啡閱讀 1,107評(píng)論 0 1
  • There are six kinds of coins in Canadian dollar, they are...
    懸崖上的小樹閱讀 439評(píng)論 3 6

友情鏈接更多精彩內(nèi)容