紳士向純原生250行拼圖小遊戲
阿新 • • 發佈:2018-10-13
ati eight 開心 所有 rec 數據 etc 關註 切片
拼圖小遊戲
先來預覽,咳咳,這個比上次那個地鼠會好看點……
代碼是可以設置難度的,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>
header好理解,註意其中的“新標簽打開圖片”相當於過關福利,平常是隱藏的。
之所以內容這麽少,是因為主邏輯這一塊的html代碼許多屬性都是動態的,所以寫死沒有價值,需要在js裏面動態生成與刪除,所以基本都移到js裏面了,這裏只要看到幾個容器就行。其中#cut-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個容器(本例用的是button)分別展示了不同圖片的一部分,然後控制相關的容器即可。
所有容器的位置都是左上角,設置偏移量使其在各個位置上,具體設置方法在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)" style="${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就麻煩許多許多了,邏輯和功能匹配,還要用到一些冷門的知識,比如styleSheets相關知識,一直用框架,都快忘光了。
說起來簡單,就是把前後選中的容器進行transform的替換。但是需要註意是基礎的業務邏輯:
第一次和下一次點擊的是同一個,那麽是要取消選中。
交換後,需要兩個都取消選中。
重置遊戲需要情況上一輪的樣式,重新排版。
遊戲過關的業務邏輯。
遊戲難易度配置。
過關獎勵,嘿嘿嘿。
等等等等。
具體基本邏輯都在代碼裏面,相關註釋也有加上,喜歡喜歡的小夥伴仔細看看,試試手,練一練。
在這裏就不長篇贅述了。
祝你玩的開心。
GitHub源碼
在線試玩
紳士向純原生250行拼圖小遊戲