摘要:前言為什么閑著沒事要做一個(gè)呢還不是前端星計(jì)劃春招實(shí)習(xí)生要我做的。目前已知的是微信內(nèi)置瀏覽器的轉(zhuǎn)碼問題這個(gè)因?yàn)槲覒械谜粋€(gè)域名,所以它為了安全就會(huì)進(jìn)行轉(zhuǎn)碼,沒法游戲。
前言
為什么閑著沒事要做一個(gè)2048呢?還不是360前端星計(jì)劃(2018春招實(shí)習(xí)生)要我做的。然后就花了幾天時(shí)間做了一個(gè)2048小游戲,兼容到pc端和部分移動(dòng)端(設(shè)備有限,有的移動(dòng)瀏覽器真的沒兼容到或者是真的不想做兼容了)。僅供大家看看就好哈。
github地址: https://github.com/GDUTxxZ/20...
在線預(yù)覽:http://47.94.199.75/index.html (這個(gè)網(wǎng)址暫時(shí)有效。。以后點(diǎn)進(jìn)去不知道又是我的什么實(shí)驗(yàn)作品。)
游戲介紹我做完給朋友看之后發(fā)現(xiàn)不是每個(gè)人都玩過這個(gè)游戲。簡(jiǎn)單介紹一下游戲內(nèi)容好了。
獲勝條件: 拼湊出一個(gè)2048方塊
失敗條件: 當(dāng)前沒有可用方塊,并且所有方塊都不可以和臨近方塊合并
index.html:游戲失敗#bg為背景圖,也就是空的灰色方塊,因?yàn)榉綁K移動(dòng)的時(shí)候不能露出底下的空白
#main為實(shí)體,也就是游戲中我們看見的包含數(shù)字的方塊
#alert為提示框,一開始display:none,當(dāng)游戲勝利或者結(jié)束的時(shí)候,display:block
#alert span 失敗消息或者勝利消息css的話,主要是關(guān)于動(dòng)畫元素的設(shè)置:
base.css #main .item { transition: all .3s ease-out; -moz-transition: all .3s ease-out; /* Firefox 4 */ -webkit-transition: all .3s ease-out; /* Safari 和 Chrome */ -o-transition: all .3s ease-out; /* Opera */ left: 0px; top: 0px; }js的話主要是兩塊,util.js負(fù)責(zé)了一些外圍函數(shù)(重要的是關(guān)于移動(dòng)端滑動(dòng)事件的封裝)的處理,2048.js就是頁面整體邏輯
util.js // 關(guān)于移動(dòng)端滑動(dòng)事件的封裝 const touchManager = (function () { let start = [] let end = [] let timeStamp = 0 let manager = {} manager.touchstart = function (event) { // 記錄下開始位置 event.stopPropagation() timeStamp = event.timeStamp // 獲取點(diǎn)擊時(shí)的時(shí)間 let target = event.targetTouches[0] start = [target.pageX, target.pageY] end = [target.pageX, target.pageY] console.log("start") } manager.touchmove = function (event) { // 記錄下移動(dòng)位置 event.stopPropagation() event.preventDefault() let target = event.targetTouches[0] end = [target.pageX, target.pageY] console.log("move") } manager.touchend = function (event) { // 處理開始位置和移動(dòng)位置給出滑動(dòng)方向 event.stopPropagation() event.preventDefault() const abs = Math.abs let time = event.timeStamp - timeStamp // 獲取滑動(dòng)操作使用的時(shí)間 let moveX = end[0] - start[0] let moveY = end[1] - start[1] if (time > 500 || (abs(moveX) < 50 && abs(moveY) < 50)) { // 移動(dòng)距離不夠或時(shí)間太長(zhǎng)就不認(rèn)為是滑動(dòng) return false } else { if (abs(moveX) >= abs(moveY)) { // 橫向移動(dòng)距離較長(zhǎng) console.log(moveX) return moveX > 0 ? "right" : "left" } else { // 縱向移動(dòng)距離較長(zhǎng) console.log(moveY) return moveY > 0 ? "down" : "up" } } } return manager })()2048.js 主要由以下幾個(gè)數(shù)據(jù)結(jié)構(gòu)和函數(shù)構(gòu)成:
數(shù)據(jù)結(jié)構(gòu):
let data = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] // 初始化 let emptyList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] // 當(dāng)前沒有元素的格子 const container = document.querySelector("#main") // 操作實(shí)體 const bg = document.querySelector("#bg") // 背景板 const alert = document.querySelector("#alert") // 提醒框 const alertText = alert.querySelector("span") // 提醒框里的文字容器 const baseSize = parseInt(getDomStyle(container).width) // 基礎(chǔ)畫板的size let gameOver = false // 標(biāo)志游戲是否結(jié)束 const animateTime = 300 // 單位ms, 動(dòng)畫時(shí)長(zhǎng)這個(gè)地方保存了大量dom元素的引用,為了以后操作的時(shí)候減少獲取dom的性能消耗
另外還有以下幾個(gè)函數(shù):
main // 主程序 resize(el) // 調(diào)整元素寬高一致 createElement() // 在emptyList里找1-2個(gè)下標(biāo)出來,給data添加新的元素,取值為{2, 4} paint(el, data) // 用data在el里畫出每一個(gè)格子 animate(move, arrow) // 傳入一個(gè)移動(dòng)隊(duì)列,和移動(dòng)方向‘left’代表橫向,‘top’代表縱向 isWin(data) isLost(data, emptyList) // 判斷游戲勝負(fù) win() lost() // 顯示勝負(fù)消息 replay() // 再來一局 moveHandle = {...} // 封裝了計(jì)算移動(dòng)結(jié)果的函數(shù)然后再?gòu)闹鞒绦蚩春瘮?shù)的流程:
function main () { // 主程序 // 調(diào)整背景和實(shí)體寬高 resize(container) resize(bg) // 初始化背景和實(shí)體元素 paint(bg, data) // 創(chuàng)建1-2個(gè)初始元素 createNewElement() paint(container, data) // 綁定事件監(jiān)聽器 addEvent(window, "keydown", function (event) { // 按鍵監(jiān)聽 if (gameOver) { return } let arrow = keyCodeMap[event.keyCode] switch (arrow) { case "left": case "up": case "right": case "down": { moveHandle.move(arrow) break } } }) addEvent(alert.querySelector("button"), "click", replay) // 再玩一次 addEvent(container, "touchstart", touchManager.touchstart) addEvent(container, "touchmove", touchManager.touchmove) addEvent(container, "touchend", function (event) { let arrow = touchManager.touchend(event) if (arrow) { moveHandle.move(arrow) } }) }也即是:1.初始化 2. 綁定事件監(jiān)聽
然后就是如何計(jì)算出移動(dòng)結(jié)果,以下用一個(gè)左滑計(jì)算(moveHandle.moveleft)為例子
moveleft: function () { // 向左移動(dòng) // 計(jì)算移動(dòng)后的data // 要移動(dòng)的元素的移動(dòng)坐標(biāo) // 沒有元素的格子 let newData = copy(data) // 獲取當(dāng)前數(shù)據(jù)的一個(gè)copy let move = [] // 方塊移動(dòng)隊(duì)列 emptyList = [] for (let i = 0; i < 4; i++) { // 一行行處理 let newList = [] // 新行 let oldList = data[i] for (let j = 0; j < 4; j++) { // 找到所有非0單元 let value = newData[i][j] if (value !== 0) { newList.push(value) } } if (newList.length > 1) { // 合并同類項(xiàng) for (let j = 0, len = newList.length; j < len - 1; j++) { if (newList[j] === newList[j + 1]) { newList[j] *= 2 newList[j + 1] = 0 j++ } } newList = newList.filter(item => item !== 0) // 過濾掉上一步產(chǎn)生的0 } for (let j = newList.length; j < 4; j++) { // 補(bǔ)全數(shù)列尾部的0 emptyList.push(i * 4 + j) newList.push(0) } newData[i] = newList // 產(chǎn)生每位元素移動(dòng)的坐標(biāo) for (let j = 0, k = 0,tag = false; j < 4; j++) { // j為舊元素位置,k為移動(dòng)到的位置 if (newList[k] === 0) { // 如果沒有要移動(dòng)的位置了 break } else if (oldList[j] === newList[k]) { // j移動(dòng)到k位置 if (j !== k) { move.push({ start: [i, j], end: [i, k] }) } k++ } else if (oldList[j] === newList[k] / 2) { // 兩個(gè)元素合成k位置的元素 move.push({ start: [i, j], end: [i, k] }) if (tag) { k++ } tag = !tag } } } return { newData: newData, move: move } }這個(gè)函數(shù)最后產(chǎn)出的是 newData 計(jì)算后的 data, move 方塊的移動(dòng)隊(duì)列,形如[{start: [x1, y1], end: [x2, y2]}, ... ]
然后怎么利用這個(gè)計(jì)算結(jié)果呢,看moveHandle.move.(moveHandle中有三個(gè)私有變量,moving鎖定句柄,防止動(dòng)畫過程中用戶再次滑動(dòng),win是否勝利,lost時(shí)候失敗)
move: function (arrow) { // arrow = 移動(dòng)方向 if (this.moving) { // 如果正在進(jìn)行動(dòng)畫,返回移動(dòng)失敗 return false } let result = this["move" + arrow]() // 獲取移動(dòng)計(jì)算后的結(jié)果 let newData = result.newData // 移動(dòng)后的數(shù)據(jù)矩陣 let move = result.move // 移動(dòng)元素列表 // 根據(jù)移動(dòng)元素列表判斷該操作是否有效 if (move.length === 0) { // 沒有可以移動(dòng)的元素,則無效 console.log("本次移動(dòng)無效") return false } // 進(jìn)行0.3秒動(dòng)畫 data = newData // 修改全局?jǐn)?shù)據(jù)矩陣 createNewElement() // 創(chuàng)造新元素 // 判斷游戲勝負(fù) this.win = isWin(newData) if (!this.win) { this.lost = isLost(newData, emptyList) } this.moving = true // 鎖定該事件句柄 setTimeout((function (self) { animate(move, arrow) return function () { // 足夠時(shí)間后 self.moving = false // 終止動(dòng)畫 paint(container, data) // 重繪視圖 // 判斷游戲勝負(fù) if (self.win) { // 贏得了游戲 win() } else if (self.lost) { lost() } } })(this), animateTime) }我自認(rèn)為我的注釋內(nèi)容還是挺多的,應(yīng)該還是能看懂。這次分享就到這了。歡迎評(píng)論區(qū)留言討論。發(fā)現(xiàn)有什么bug也盡可能跟我說把。
BUG目前已知的是:
1.微信內(nèi)置瀏覽器的轉(zhuǎn)碼問題:這個(gè)因?yàn)槲覒械谜粋€(gè)域名,所以它為了安全就會(huì)進(jìn)行轉(zhuǎn)碼,沒法游戲。也就不修復(fù)了。。只是個(gè)小玩具。2.ios長(zhǎng)按會(huì)選取文字而且無法取消:
這個(gè)問題我已經(jīng)做了一定的修復(fù),但是我沒復(fù)現(xiàn)這個(gè)問題的方法,也沒再處理3.夸克瀏覽器自帶手勢(shì)導(dǎo)致左滑右滑會(huì)進(jìn)行系統(tǒng)行為:
沒想到辦法,如果有人有辦法請(qǐng)告訴我,謝謝。文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/94344.html
相關(guān)文章
前端純原生代碼實(shí)現(xiàn)2048
摘要:前言為什么閑著沒事要做一個(gè)呢還不是前端星計(jì)劃春招實(shí)習(xí)生要我做的。目前已知的是微信內(nèi)置瀏覽器的轉(zhuǎn)碼問題這個(gè)因?yàn)槲覒械谜粋€(gè)域名,所以它為了安全就會(huì)進(jìn)行轉(zhuǎn)碼,沒法游戲。 前言 為什么閑著沒事要做一個(gè)2048呢?還不是360前端星計(jì)劃(2018春招實(shí)習(xí)生)要我做的。然后就花了幾天時(shí)間做了一個(gè)2048小游戲,兼容到pc端和部分移動(dòng)端(設(shè)備有限,有的移動(dòng)瀏覽器真的沒兼容到或者是真的不想做兼容了)...
前端純原生代碼實(shí)現(xiàn)2048
摘要:前言為什么閑著沒事要做一個(gè)呢還不是前端星計(jì)劃春招實(shí)習(xí)生要我做的。目前已知的是微信內(nèi)置瀏覽器的轉(zhuǎn)碼問題這個(gè)因?yàn)槲覒械谜粋€(gè)域名,所以它為了安全就會(huì)進(jìn)行轉(zhuǎn)碼,沒法游戲。 前言 為什么閑著沒事要做一個(gè)2048呢?還不是360前端星計(jì)劃(2018春招實(shí)習(xí)生)要我做的。然后就花了幾天時(shí)間做了一個(gè)2048小游戲,兼容到pc端和部分移動(dòng)端(設(shè)備有限,有的移動(dòng)瀏覽器真的沒兼容到或者是真的不想做兼容了)...
我的一些開源項(xiàng)目(前端)
摘要:于是,我決定厚著臉皮來宣傳一下我的幾個(gè)開源項(xiàng)目,雖然大多數(shù)都是一些比較簡(jiǎn)單的游戲,但是這可以讓更多人看到我的項(xiàng)目,也可以讓我自己知道哪里地方做得不好,并且加以改進(jìn)。正文清技背單詞使用開發(fā)的背單詞應(yīng)用,開發(fā)時(shí)間為一個(gè)月,目前是版本。 前言 之前陸陸續(xù)續(xù)在 GitHub 上創(chuàng)建了幾個(gè)項(xiàng)目,奈何沒人關(guān)注(可能我的項(xiàng)目太垃圾了)。于是,我決定厚著臉皮來宣傳一下我的幾個(gè)開源項(xiàng)目,雖然大多數(shù)都是一...
我的一些開源項(xiàng)目(前端)
摘要:于是,我決定厚著臉皮來宣傳一下我的幾個(gè)開源項(xiàng)目,雖然大多數(shù)都是一些比較簡(jiǎn)單的游戲,但是這可以讓更多人看到我的項(xiàng)目,也可以讓我自己知道哪里地方做得不好,并且加以改進(jìn)。正文清技背單詞使用開發(fā)的背單詞應(yīng)用,開發(fā)時(shí)間為一個(gè)月,目前是版本。 前言 之前陸陸續(xù)續(xù)在 GitHub 上創(chuàng)建了幾個(gè)項(xiàng)目,奈何沒人關(guān)注(可能我的項(xiàng)目太垃圾了)。于是,我決定厚著臉皮來宣傳一下我的幾個(gè)開源項(xiàng)目,雖然大多數(shù)都是一...
我的一些開源項(xiàng)目(前端)
摘要:于是,我決定厚著臉皮來宣傳一下我的幾個(gè)開源項(xiàng)目,雖然大多數(shù)都是一些比較簡(jiǎn)單的游戲,但是這可以讓更多人看到我的項(xiàng)目,也可以讓我自己知道哪里地方做得不好,并且加以改進(jìn)。正文清技背單詞使用開發(fā)的背單詞應(yīng)用,開發(fā)時(shí)間為一個(gè)月,目前是版本。 前言 之前陸陸續(xù)續(xù)在 GitHub 上創(chuàng)建了幾個(gè)項(xiàng)目,奈何沒人關(guān)注(可能我的項(xiàng)目太垃圾了)。于是,我決定厚著臉皮來宣傳一下我的幾個(gè)開源項(xiàng)目,雖然大多數(shù)都是一...
發(fā)表評(píng)論
0條評(píng)論
閱讀 1847·2021-11-22 15:25
閱讀 3912·2021-11-17 09:33
閱讀 2507·2021-10-12 10:12
閱讀 1802·2021-10-09 09:44
閱讀 3235·2021-10-08 10:04
閱讀 1313·2021-09-29 09:35
閱讀 1947·2019-08-30 12:57
閱讀 1303·2019-08-29 16:22