2048+html源码之家,前端纯原生代码实现2048
前言
為什么閑著沒事要做一個2048呢?還不是360前端星計劃(2018春招實(shí)習(xí)生)要我做的。然后就花了幾天時間做了一個2048小游戲,兼容到pc端和部分移動端(設(shè)備有限,有的移動瀏覽器真的沒兼容到或者是真的不想做兼容了)。僅供大家看看就好哈。
在線預(yù)覽:http://47.94.199.75/index.html (這個網(wǎng)址暫時有效。。以后點(diǎn)進(jìn)去不知道又是我的什么實(shí)驗(yàn)作品。)
游戲介紹
我做完給朋友看之后發(fā)現(xiàn)不是每個人都玩過這個游戲。簡單介紹一下游戲內(nèi)容好了。
獲勝條件: 拼湊出一個2048方塊
失敗條件: 當(dāng)前沒有可用方塊,并且所有方塊都不可以和臨近方塊合并
代碼結(jié)構(gòu)
index.html:
游戲失敗
再來一局
#bg為背景圖,也就是空的灰色方塊,因?yàn)榉綁K移動的時候不能露出底下的空白
#main為實(shí)體,也就是游戲中我們看見的包含數(shù)字的方塊
#alert為提示框,一開始display:none,當(dāng)游戲勝利或者結(jié)束的時候,display:block
#alert span 失敗消息或者勝利消息
css的話,主要是關(guān)于動畫元素的設(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)于移動端滑動事件的封裝)的處理,2048.js就是頁面整體邏輯
util.js // 關(guān)于移動端滑動事件的封裝
const touchManager = (function () {
let start = []
let end = []
let timeStamp = 0
let manager = {}
manager.touchstart = function (event) { // 記錄下開始位置
event.stopPropagation()
timeStamp = event.timeStamp // 獲取點(diǎn)擊時的時間
let target = event.targetTouches[0]
start = [target.pageX, target.pageY]
end = [target.pageX, target.pageY]
console.log('start')
}
manager.touchmove = function (event) { // 記錄下移動位置
event.stopPropagation()
event.preventDefault()
let target = event.targetTouches[0]
end = [target.pageX, target.pageY]
console.log('move')
}
manager.touchend = function (event) { // 處理開始位置和移動位置給出滑動方向
event.stopPropagation()
event.preventDefault()
const abs = Math.abs
let time = event.timeStamp - timeStamp // 獲取滑動操作使用的時間
let moveX = end[0] - start[0]
let moveY = end[1] - start[1]
if (time > 500 || (abs(moveX) < 50 && abs(moveY) < 50)) { // 移動距離不夠或時間太長就不認(rèn)為是滑動
return false
} else {
if (abs(moveX) >= abs(moveY)) { // 橫向移動距離較長
console.log(moveX)
return moveX > 0 ? 'right' : 'left'
} else { // 縱向移動距離較長
console.log(moveY)
return moveY > 0 ? 'down' : 'up'
}
}
}
return manager
})()
2048.js 主要由以下幾個數(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, 動畫時長
這個地方保存了大量dom元素的引用,為了以后操作的時候減少獲取dom的性能消耗
另外還有以下幾個函數(shù):
main // 主程序
resize(el) // 調(diào)整元素寬高一致
createElement() // 在emptyList里找1-2個下標(biāo)出來,給data添加新的元素,取值為{2, 4}
paint(el, data) // 用data在el里畫出每一個格子
animate(move, arrow) // 傳入一個移動隊列,和移動方向‘left’代表橫向,‘top’代表縱向
isWin(data) isLost(data, emptyList) // 判斷游戲勝負(fù)
win() lost() // 顯示勝負(fù)消息
replay() // 再來一局
moveHandle = {...} // 封裝了計算移動結(jié)果的函數(shù)
然后再從主程序看函數(shù)的流程:
function main () { // 主程序
// 調(diào)整背景和實(shí)體寬高
resize(container)
resize(bg)
// 初始化背景和實(shí)體元素
paint(bg, data)
// 創(chuàng)建1-2個初始元素
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)聽
然后就是如何計算出移動結(jié)果,以下用一個左滑計算(moveHandle.moveleft)為例子
moveleft: function () { // 向左移動
// 計算移動后的data
// 要移動的元素的移動坐標(biāo)
// 沒有元素的格子
let newData = copy(data) // 獲取當(dāng)前數(shù)據(jù)的一個copy
let move = [] // 方塊移動隊列
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) { // 合并同類項
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)生每位元素移動的坐標(biāo)
for (let j = 0, k = 0,tag = false; j < 4; j++) { // j為舊元素位置,k為移動到的位置
if (newList[k] === 0) { // 如果沒有要移動的位置了
break
} else if (oldList[j] === newList[k]) { // j移動到k位置
if (j !== k) {
move.push({
start: [i, j],
end: [i, k]
})
}
k++
} else if (oldList[j] === newList[k] / 2) { // 兩個元素合成k位置的元素
move.push({
start: [i, j],
end: [i, k]
})
if (tag) {
k++
}
tag = !tag
}
}
}
return {
newData: newData,
move: move
}
}
這個函數(shù)最后產(chǎn)出的是 newData 計算后的 data, move 方塊的移動隊列,形如[{start: [x1, y1], end: [x2, y2]}, ... ]
然后怎么利用這個計算結(jié)果呢,看moveHandle.move.(moveHandle中有三個私有變量,moving鎖定句柄,防止動畫過程中用戶再次滑動,win是否勝利,lost時候失敗)
move: function (arrow) { // arrow = 移動方向
if (this.moving) { // 如果正在進(jìn)行動畫,返回移動失敗
return false
}
let result = this['move' + arrow]() // 獲取移動計算后的結(jié)果
let newData = result.newData // 移動后的數(shù)據(jù)矩陣
let move = result.move // 移動元素列表
// 根據(jù)移動元素列表判斷該操作是否有效
if (move.length === 0) { // 沒有可以移動的元素,則無效
console.log('本次移動無效')
return false
}
// 進(jìn)行0.3秒動畫
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 () {
// 足夠時間后
self.moving = false // 終止動畫
paint(container, data) // 重繪視圖
// 判斷游戲勝負(fù)
if (self.win) { // 贏得了游戲
win()
} else if (self.lost) {
lost()
}
}
})(this), animateTime)
}
我自認(rèn)為我的注釋內(nèi)容還是挺多的,應(yīng)該還是能看懂。這次分享就到這了。歡迎評論區(qū)留言討論。發(fā)現(xiàn)有什么bug也盡可能跟我說把。
BUG
目前已知的是:
1.微信內(nèi)置瀏覽器的轉(zhuǎn)碼問題:
這個因?yàn)槲覒械谜粋€域名,所以它為了安全就會進(jìn)行轉(zhuǎn)碼,沒法游戲。也就不修復(fù)了。。只是個小玩具。
2.ios長按會選取文字而且無法取消:
這個問題我已經(jīng)做了一定的修復(fù),但是我沒復(fù)現(xiàn)這個問題的方法,也沒再處理
3.夸克瀏覽器自帶手勢導(dǎo)致左滑右滑會進(jìn)行系統(tǒng)行為:
沒想到辦法,如果有人有辦法請告訴我,謝謝。
總結(jié)
以上是生活随笔為你收集整理的2048+html源码之家,前端纯原生代码实现2048的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于python实现网络课程秒刷系列二
- 下一篇: MySQL(1)CentOS彻底卸载My