紳士向,純原生250行,拼圖小遊戲
來自專欄猿論7 人贊了文章
拼圖小遊戲
先來預覽,咳咳,這個比上次那個地鼠會好看點......
代碼是可以設置難度的,3就是9,9-就是81 ......
相比來說,此程序難度可是遠遠高過打地鼠的,希望小夥伴能跟上?
HTML
<header> <button =Game.restart()>重新開始</button> <button id="download" =Game.openImage()>新標籤打開圖片</button></header><main> <section class="game-area"> <img id=background-img src="#" alt="backgroundImg"> <div id="cut-imgs"></div> </section></main>
頭好理解,注意其中的「新標籤打開圖片」相當於過關福利,平常是隱藏的。
之所以內容這麼少,是因為主邏輯這一塊的HTML代碼許多屬性都是動態的,所以寫死沒有價值,需要在JS裡面動態生成與刪除,所以基本都移到JS裡面了,這裡只要看到幾個容器就行。其中#切IMGS是下面遊戲的容器。
CSS
.cut-img { position: absolute; top: 0; left: 0; border: 0; padding: 0; transition: transform .3s linear; box-sizing: border-box;}html,body { height: 100%; margin: 0;}body { display: flex; flex-direction: column; justify-content: center; align-items: center;}header,main,footer { width: 50%;}header { display: flex; justify-content: space-between;}main { position: relative; height: auto;}.game-area { position: relative; height: auto;}#background-img { max-width: 100%; max-height: 100%; vertical-align: top; opacity: 0;}#cut-imgs { position: absolute; top: 0; left: 0; display: flex; flex-wrap: wrap; width: 100%; height: 100%;}button:focus { outline: none;}.selected { border: 1px solid blue;}#download { display: none;}
CSS裡面注意動畫的設置,還有切片圖像的處理。
這裡相信第一反應下面是把一整張圖切9份。其實不然,不過是9個容器(本例用的是按鈕)分別展示了不同圖片的一部分,然後控制相關的容器即可。
所有容器的位置都是左上角,設置偏移量使其在各個位置上,具體設置方法在JS裡面。
JS
const Game = { // 重新開始遊戲 restart() { // 清空已有數據,重置按鈕 this.reset() const level = this.config.level // 計算position的參數 const positionParam = 1 / (level - 1) * 100 const imgUrl = this.config.imgUrl = `https://h5games-dom.oss-cn-hangzhou.aliyuncs.com/puzzle/${~~(Math.random() * 5)}.png` const backgroundImg = document.querySelector(#background-img) backgroundImg.src = imgUrl // 獲取樣式表 const styleSheet = this.config.imgCutStyle = document.styleSheets[0] // 如果添加過自定義則刪除 let firstRule = styleSheet.rules[0] if (firstRule.selectorText === .custom) styleSheet.deleteRule(0) let scale = 1 / this.config.level * 100 + % styleSheet.insertRule(`.custom { width: ${scale}; height: ${scale}; background: url(${imgUrl}) no-repeat; background-size: ${this.config.level * 100}%; }`, 0) backgroundImg. = () => { for (let i = 0, j = Math.pow(this.config.level, 2); i < j; i++) { this.config.cutImgsCountArray.push(i) } // DOM字元串 let cutImgsStr = this.getInitialSort() this.config.cutImgsCountArray.forEach((num, index) => { // 保存正確的變化,做判斷是否獲勝的基礎 this.config.trueTransforms.push(`translate(${index % level * 100}%, ${~~(index / level) % level * 100}%)`) // 這裡設置會變動的style const transform = `transform: translate(${num % level * 100}%, ${~~(num / level) % level * 100}%);` const backgroundPosition = `background-position: ${index % level * positionParam}% ${~~(index / level) % level * positionParam}%;` // 全部在左上初始位置,設置偏移量即可 cutImgsStr += `<button class="cut-img custom" data-index=${index} ="Game.click(event)" stylex="${transform + backgroundPosition}"></button>` }) document.querySelector(#cut-imgs).innerHTML = cutImgsStr this.instance.cutImgs = document.querySelectorAll(.cut-img) } }, // 點擊圖片 click(e) { const index = e.target.dataset.index // 第一次點擊直接結束 if (this.tool.currentIndex === -1) { this.getCutImg(index).classList.add(selected) this.tool.currentIndex = index return } const oldCutImg = this.getCutImg(this.tool.currentIndex) // 如果點擊不是同一個再走邏輯 if (this.tool.currentIndex === index) { this.getCutImg(index).classList.remove(selected) this.tool.currentIndex = -1 } else { const newCutImg = this.getCutImg(index) const [a, b] = [newCutImg.style.transform, oldCutImg.style.transform] oldCutImg.style.transform = a newCutImg.style.transform = b this.tool.currentIndex = -1 setTimeout(() => { download.style.display = none oldCutImg.classList.remove(selected) newCutImg.classList.remove(selected) if (this.checkNoWin()) console.log(NoWin) else { download.style.display = block alert(win) } }, 500); } }, // 獲取實例 getCutImg(index) { return this.instance.cutImgs[index] }, // 獲取初始的正確排序 getInitialSort() { const cal = arr => { let length = arr.length let reverse = 0 for (let i = 0; i < length - 1; i++) { let n = arr[i] for (let j = i + 1; j < length; j++) { let m = arr[j] if (n > m) reverse += 1 } } return reverse } // 數組隨機排序 const randomSort = (a, b) => Math.random() > 0.5 ? -1 : 1 // 循環直到獲取可還原的排序 while (1) { if (cal(this.config.cutImgsCountArray.sort(randomSort)) % 2 === 0) return } }, // 檢查是否還沒勝利 checkNoWin() { let cutImgs = this.instance.cutImgs let trueTransforms = this.config.trueTransforms for (let i = 0, j = this.instance.cutImgs.length; i < j; i++) { if (cutImgs[i].style.transform !== trueTransforms[i]) return true } }, // 清空已有數據 reset() { let resetParam = this.resetParam this.config = this.deepCopy(resetParam.config) this.instance = this.deepCopy(resetParam.instance) this.tool = this.deepCopy(resetParam.tool) download.style.display = none }, deepCopy(obj) { return JSON.parse(JSON.stringify(obj)) }, // 打開圖片 openImage() { window.open(this.config.imgUrl) }, // 重置時候的初始化參數 resetParam: { // 配置 config: { level: 3, cutImgsCountArray: [], trueTransforms: [], imgCutStyle: {}, imgUrl: , }, // 實例 instance: { // 所有圖片的實例 cutImgs: [], }, // 記錄工具 tool: { currentIndex: -1 }, }}Game.restart()
JS就麻煩許多許多了,邏輯和功能匹配,還要用到一些冷門的知識,比如樣式表相關知識,一直用框架,都快忘光了。
說起來簡單,就是把前後選中的容器進行改造的替換但是需要注意是基礎的業務邏輯:
- 第一次和下一次點擊的是同一個,那麼是要取消選中。
- 交換後,需要兩個都取消選中。
- 重置遊戲需要情況上一輪的樣式,重新排版。
- 遊戲過關的業務邏輯。
- 遊戲難易度配置。
- 過關獎勵,嘿嘿嘿。
- 等等等等。
具體基本邏輯都在代碼裡面,相關注釋也有加上,喜歡喜歡的小夥伴仔細看看,試試手,練一練。
在這裡就不長篇贅述了。
祝你玩的開心。
GitHub的源碼
在線試玩
喜歡的話記得關注一波,定期更新技術文章,滿滿的都是乾貨。
作者:風藍小棲
鏈接:https://www.imooc.com/article/70046
來源:慕課網
推薦閱讀:
在做程序員的道路上,你掌握了什麼概念或技術使你感覺自我提升突飛猛進?
你看過/寫過哪些有意思的代碼?
如何在程序里留下彩蛋?
為什麼部分程序員下班後只關顯示器不關電腦?
有哪些好笑的關於程序員的笑話?
如何防止自己被人肉搜索到?
面試必備之樂觀鎖與悲觀鎖
搞定計算機網路面試,看這篇就夠了(補充版)
哪些 Python 庫讓你相見恨晚?
作為程序員的你,常用的工具軟體有哪些?
推薦閱讀:
※吃雞一定要組隊,才有感覺
※一個來自R6S老玩家的勸退
※武漢完美空間:第五人格:求生者獲勝的三大因素,走位成為制勝關鍵
※聽說,SuperMajor,血魔和獸王更配哦