游戲簡介
漢諾塔是源于印度一個古老傳說的益智游戲将鸵,傳說大梵天創(chuàng)造世界的時候順便搞了三根柱子,一根柱子上摞著一堆從大到小的圓環(huán)佑颇,他命令婆羅門把圓環(huán)全部移動到另一個柱子上顶掉,依舊是從大到小,且移動規(guī)則如下:
1.一次只能把一個圓環(huán)從一根柱子移動到另一根柱子上
2.圓環(huán)的上面不能放比它大的圓環(huán)
詳細(xì)介紹及解法請參考文章:漢諾塔與遞歸挑胸。
最終的成果示例請點擊:漢諾塔小游戲痒筒。
溫馨提示:本篇教程屬于從頭到尾面面俱到型,雖然開發(fā)上本身是沒什么難度的茬贵,但不妨礙把它做成一個很完善的游戲簿透,所以它很長。
布局
本項目使用vue作為基礎(chǔ)框架解藻。
使用這些視圖框架的主要思想就是操作數(shù)據(jù)老充,視圖更新交給框架,只要做好數(shù)據(jù)和視圖的映射即可螟左,所以本游戲的核心也就是維護(hù)一些數(shù)據(jù)及操作數(shù)據(jù)啡浊。
首先要做的是布局,要模擬出上圖中的三根柱子及圓環(huán)胶背。本游戲全部使用DOM來布局虫啥,不使用canvas。
柱子的布局很簡單奄妨,用div元素來作為線段涂籽,代碼如下:
<template>
<div class="container">
<div class="(column, cIndex)" v-for="item in columnList" :key="item.name">
<div class="col"></div>
<div class="land"></div>
<div class="name">{{item.name}}</div>
</div>
</div>
</template>
<script>
export default {
name: 'Game',
data() {
return {
columnList: [
{
name: '起始柱'
},
{
name: '中轉(zhuǎn)柱'
},
{
name: '終點柱'
}
]
}
}
}
</script>
樣式部分很簡單就不列出來了,效果如下:
接下來是圓環(huán)砸抛,因為有三根柱子评雌,所以使用三個數(shù)組來存放树枫,每個圓環(huán)用一個對象來表示,每個圓環(huán)有顏色景东、代表大小的序號屬性砂轻,序號從1開始,1代表最大斤吐,因為圓環(huán)數(shù)量可變搔涝,所以每個圓環(huán)的寬高、位置都需要動態(tài)進(jìn)行計算和措,渲染同樣是循環(huán)進(jìn)行渲染庄呈,三個圓環(huán)的情況如下所示:
<template>
<div class="container">
<div class="column" v-for="(item, cIndex) in columnList" :key="item.name">
<!--省略...-->
<div class="ringsBox">
<div
class="ring"
v-for="(ringItem, index) in ringList[item.prop]"
:key="ringItem.order"
:style="{
width: (wsize - (ringItem.order - 1) * 10) + '%',
height: hsize / ringNum + '%',
backgroundColor: ringItem.color,
left: (100 - (wsize - (ringItem.order - 1) * 10)) / 2 + '%',
bottom: (hsize / ringNum) * index + '%'
}"
></div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Game",
data() {
return {
// 柱子
// 增加了一個prop屬性,代表該柱子對應(yīng)的圓環(huán)數(shù)組
columnList: [
{
name: "起始柱",
prop: "startColRingList",
},
{
name: "中轉(zhuǎn)柱",
prop: "transferColRingList",
},
{
name: "終點柱",
prop: "endColRingList",
},
],
// 圓環(huán)
// 圓環(huán)數(shù)量
ringNum: 3,
// 圓環(huán)數(shù)據(jù)
ringList: {
startColRingList: [
{
color: "#ffa36c",
order: 1,
},
{
color: "#00bcd4",
order: 2,
},
{
color: "#848ccf",
order: 3,
}
],
transferColRingList: [],
endColRingList: [],
},
};
},
computed: {
// 最大寬度值
wsize() {
return this.ringNum <= 5 ? 50 : this.ringNum * 10
},
// 最大高度值
hsize() {
return this.ringNum <= 3 ? 30 : this.ringNum * 10
}
}
};
</script>
效果如下所示:
拖動
這個游戲主要的交互就是拖動圓環(huán)到另一根柱子上派阱,所以圓環(huán)需要支持拖動诬留,需要注意的是每根柱子上都只有最上面的一個圓環(huán)能被拖動,且拖動到的柱子上存在的最上面的圓環(huán)還要比它大贫母,否則不允許落下文兑。
具體的實現(xiàn)就是監(jiān)聽鼠標(biāo)按下事件、鼠標(biāo)移動事件腺劣、鼠標(biāo)松開事件绿贞,鼠標(biāo)按下移動時改變該圓環(huán)的transform: translate(x,y)
屬性來進(jìn)行移動,鼠標(biāo)松開時判斷當(dāng)前圓環(huán)被拖動到的位置是否在三個圓環(huán)的某一個區(qū)域內(nèi)橘原,是的話再判斷圓環(huán)能否落到該柱子上籍铁,符合條件就把該圓環(huán)的數(shù)據(jù)從之前柱子的數(shù)組移到落下柱子的數(shù)組內(nèi),否則就復(fù)位transform
屬性讓圓環(huán)回去靠柑。
綁定事件需要注意的是按下事件綁定到圓環(huán)上寨辩,而移動和松開事件要綁定到body上吓懈,否則當(dāng)你移動過快時鼠標(biāo)指針可能會和圓環(huán)不同步而超出圓環(huán)歼冰,進(jìn)而當(dāng)你松開后就監(jiān)聽不到松開事件了。
<template>
<div class="container">
<div class="column" v-for="(column, cIndex) in columnList" :key="item.name">
<!--省略...-->
<div class="ringsBox">
<div
class="ring"
v-for="(ringItem, index) in ringList[item.prop]"
<!--省略...-->
@mousedown="mousedown($event, ringItem, index, item.prop, cIndex)"
></div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Game",
// ...
mounted() {
this.bindEvent()
},
beforeDestroy() {
this.unbindEvent()
},
methods: {
// 鼠標(biāo)移動事件和松開事件綁定到body上
bindEvent() {
document.body.addEventListener('mousemove', this.mousemove)
document.body.addEventListener('mouseup', this.mouseup)
},
// 解綁事件
unbindEvent() {
document.body.removeEventListener('mousemove', this.mousemove)
document.body.removeEventListener('mouseup', this.mouseup)
}
}
};
</script>
接下來重點實現(xiàn)這三個事件處理函數(shù)耻警。
先定義一些必要的變量:
{
// 拖動變量
dragProp: '',// 當(dāng)前拖動圓環(huán)所屬的柱子
dragOrder: 0,// 當(dāng)前拖動圓環(huán)的大小序號
dragIndex: -1,// 當(dāng)前拖動圓環(huán)在原柱子上的索引
dragColumnIndex: -1,// 當(dāng)前拖動圓環(huán)所在柱子的索引
draging: false,// 當(dāng)前是否是拖動中
startPos: {// 鼠標(biāo)按下時的坐標(biāo)
x: 0,
y: 0
},
dragPos: {// 鼠標(biāo)移動的偏移量
x: 0,
y: 0
}
}
拖動是拖動當(dāng)前鼠標(biāo)按下的圓環(huán)隔嫡,因為是在循環(huán)體里添加的css屬性,所以對所有圓環(huán)都是有效的甘穿,那么怎么判斷目標(biāo)圓環(huán)是哪個圓環(huán)腮恩,對于圓環(huán)來說,它的order
屬性是唯一的温兼,所以根據(jù)dragOrder
變量就可以定位到了秸滴,是的話就讓它的translate
的值隨著dragPos
的值進(jìn)行變化:
<template>
<div class="container">
<div class="column" v-for="(column, cIndex) in columnList" :key="item.name">
<!--省略...-->
<div class="ringsBox">
<div
class="ring"
v-for="(ringItem, index) in ringList[item.prop]"
:key="ringItem.order"
:style="{
<!--省略...-->
transform: dragOrder === ringItem.order ? `translate(${dragPos.x}px, ${dragPos.y}px)` : 'translate(0px, 0px)'
}"
></div>
</div>
</div>
</div>
</template>
鼠標(biāo)按下事件處理函數(shù)的主要邏輯是設(shè)置拖動標(biāo)志位、緩存當(dāng)前拖動的一些數(shù)據(jù)募判,比如當(dāng)前拖動圓環(huán)的相關(guān)信息及鼠標(biāo)按下的位置信息:
{
// 鼠標(biāo)按下事件
mousedown(e, ringItem, index, prop, columnIndex) {
// 當(dāng)按下的不是該柱子最上面的圓環(huán)時不做任何處理
if (index < this.ringList[prop].length - 1) {
return
}
this.dragProp = prop
this.dragOrder = ringItem.order
this.dragIndex = index
this.dragColumnIndex = columnIndex
this.startPos.x = e.clientX
this.startPos.y = e.clientY
this.draging = true
}
}
鼠標(biāo)移動事件處理函數(shù)的功能是實時更新拖動的偏移量荡含,圓環(huán)就會跟著動了:
{
// 鼠標(biāo)移動事件
mousemove(e) {
// 不是拖動的情況直接返回
if (!this.draging) {
return
}
this.dragPos.x = e.clientX - this.startPos.x
this.dragPos.y = e.clientY - this.startPos.y
}
}
鼠標(biāo)松開事件是最重要的咒唆,在該函數(shù)里需要判斷圓環(huán)是否拖動到某個柱子區(qū)域內(nèi)及能否落下及具體的落下操作:
{
// 鼠標(biāo)松開事件
mouseup() {
// 不是拖動的情況直接返回
if (!this.draging) {
return
}
// 復(fù)位拖動標(biāo)志位
this.draging = false
// 計算圓環(huán)拖動到哪個柱子上
let columnIndex = this.checkInColumnIndex(this.dragOrder)
// 判斷圓環(huán)是否可以落到該柱子上
let canDraged = this.canDraged(columnIndex, this.dragOrder)
// 能落下的話就移動該圓環(huán)的數(shù)據(jù)
if (canDraged) {
this.dragToColumn(columnIndex, this.dragProp, this.dragIndex)
}
// 復(fù)位
this.reset()
}
}
接下來一步步來實現(xiàn)該函數(shù)里的幾個方法。
因為涉及到位置計算释液,所以需要獲取實際的DOM元素全释,先在模板里加上ref用于引用DOM:
<template>
<div class="container">
<div class="column" v-for="(item, cIndex) in columnList" :key="item.name" :ref="'column' + cIndex">
<div class="ringsBox">
<div
class="ring"
v-for="(ringItem, index) in ringList[item.prop]"
:ref="'ring' + ringItem.order"
></div>
</div>
</div>
</div>
</template>
首先柱子區(qū)域是一個矩形,如下所示:
然后圓環(huán)其實也是一個矩形误债,那么問題實際上就轉(zhuǎn)換為求兩個矩形是否相交浸船,這個是很簡單的,方便起見寝蹈,把它們的位置都相對于瀏覽器窗口左上角來計算李命,那么滿足下面的條件圓環(huán)和柱子區(qū)域即相交:
1.圓環(huán)的右側(cè)距窗口左側(cè)的距離大于柱子區(qū)域左側(cè)距窗口左側(cè)的距離、同時圓環(huán)左側(cè)距窗口的距離小于柱子區(qū)域右側(cè)距窗口左側(cè)的距離
2.圓環(huán)的頂部距窗口頂部的距離小于柱子區(qū)域的底部距窗口頂部的距離躺盛、同時圓環(huán)的底部距窗口頂部的距離大于柱子區(qū)域頂部距窗口頂部的距離
翻譯成代碼如下:
{
// 檢查某個圓環(huán)的位置是否在某個柱子區(qū)域內(nèi)
checkInColumnIndex(order) {
let result = -1
// 獲取圓環(huán)相當(dāng)于瀏覽器窗口的位置信息
let ringRect = this.$refs['ring' + order][0].getBoundingClientRect()
// 遍歷獲取柱子區(qū)域相當(dāng)于瀏覽器窗口的位置信息
;[0, 1, 2].forEach((index) => {
// 獲取區(qū)域位置信息
let {left, right, top, bottom} = this.$refs['column' + index][0].getBoundingClientRect()
// 重合檢查
if (
(ringRect.right >= left && ringRect.left <= right) && (ringRect.top <= bottom && ringRect.bottom >= top))
{
result = index
}
})
return result
}
}
知道了在哪個圓環(huán)后接下來要判斷是否可以落下项戴,根據(jù)游戲規(guī)則,小的圓環(huán)上不能放大的槽惫,所以判斷當(dāng)前柱子上最小的圓環(huán)是否比當(dāng)前圓環(huán)大即可:
{
// 判斷某個圓環(huán)是否可以落到指定索引的柱子上
canDraged(columnIndex, order) {
// 不在圓環(huán)區(qū)域內(nèi)直接返回
if (columnIndex === -1) {
return
}
let prop = this.columnList[columnIndex].prop
let list = this.ringList[prop]
// 柱子為空則可以落下
if (list.length <= 0) {
return true
}
// 數(shù)組里最后一項即是當(dāng)前柱子最小的圓環(huán)
let minOrder = list[list.length - 1].order
if (order > minOrder) {
return true
}
return false
}
}
判斷如果是可以落下的那么直接將該圓環(huán)的數(shù)組從原柱子數(shù)組移到目標(biāo)數(shù)組即可:
{
// 某個圓環(huán)落到指定索引的柱子上
dragToColumn(columnIndex, prop, index) {
// 從原數(shù)組取出
let ring = this.ringList[prop].splice(index, 1)[0]
// 追加到目標(biāo)數(shù)組
let toProp = this.columnList[columnIndex].prop
this.ringList[toProp].push(ring)
}
}
如果不能落下的話那么就讓圓環(huán)回去周叮,圓環(huán)的位置要回去的話直接把dragPos
的值恢復(fù)要0即可,其他的相關(guān)變量也需要復(fù)位:
{
// 拖動完成后復(fù)位
reset() {
this.dragProp = ''
this.dragOrder = 0
this.dragIndex = null
this.draging = false
this.dragColumnIndex = -1
this.startPos.x = 0
this.startPos.x = 0
this.dragPos.x = 0
this.dragPos.y = 0
}
}
到這里游戲的核心功能就完成了界斜,已經(jīng)可以玩了:
圖上的圓環(huán)移到某個區(qū)域內(nèi)顯示的背景突出效果實現(xiàn)也很簡單仿耽,在移動過程中不斷檢測是否相交,是的話就給對應(yīng)的區(qū)域加上背景的類名:
<template>
<div class="container">
<div class="column" v-for="(item, cIndex) in columnList" :key="item.name" :ref="'column' + cIndex" :class="{dragIn: dragingColumnIndex === cIndex}">
</div>
</div>
</template>
{
data() {
return {
dragingColumnIndex: -1//拖動過程中實時相交的區(qū)域索引
}
},
methods: {
mousemove(e) {
//...
this.dragingColumnIndex = this.checkInColumnIndex(this.dragOrder)
}
}
}
完成檢測
每一次拖動后都要判斷游戲是否完成各薇,判斷方式很簡單项贺,檢測目標(biāo)數(shù)組不為空,而其他兩根柱子的數(shù)組為空就可以了峭判,或者直接檢測目標(biāo)數(shù)組里的圓環(huán)數(shù)量是否和當(dāng)前層數(shù)對應(yīng)开缎,反正方式有很多。
{
// 檢測游戲是否完成
checkPass() {
if (this.ringList.endColRingList.length === this.ringNum) {
alert('恭喜你林螃,完成啦')
}
}
}
就是這么簡單奕删。
游戲基本功能到這里就結(jié)束了,但是作為一個有夢想有追求的人疗认,完成基本功能只意味著開始完残,隨便想想煞聪,就能想到還有很多能做的:游戲?qū)訑?shù)選擇妖滔、操作按鈕、信息顯示俯邓,還有一些高級功能:回退操作缎浇、自動操作扎拣、步驟回放等等,因為篇幅原因,本篇不會全部展開講解二蓝,只挑一兩個來淺析一下尊蚁,不要走開,精彩繼續(xù)侣夷。
動畫過度
首先先做個優(yōu)化,目前來說百拓,當(dāng)你拖動圓環(huán)到某個柱子上松開時圓環(huán)是瞬間顯示到柱子上的,而不是過渡過去的决帖,包括當(dāng)松開鼠標(biāo)不符合落下條件圓環(huán)回去也是一樣,突變總是不優(yōu)雅的蓖捶,我們讓它平滑的滑動起來地回。
因為圓環(huán)是使用css的translate
屬性來跟隨鼠標(biāo)動的俊鱼,所以只要給它加上transition
屬性即可平滑過渡,要注意的是拖動過程中該屬性的值必須為none
并闲,否則你每拖動一下细睡,它都要緩一下過渡過去,所以該屬性的值要動態(tài)進(jìn)行設(shè)置帝火。
圓環(huán)不符合落下條件時復(fù)位的過渡不需要修改,加上transition
就有過渡能力了蠢壹,主要是符合落下條件時從鼠標(biāo)松開的位置過渡到目標(biāo)位置需要計算一下九巡,看圖:
因為拖動中的圓環(huán)的transition
的坐標(biāo)也就是dragPos
屬性的值是相當(dāng)于鼠標(biāo)按下的位置來說的,其實也就是圓環(huán)開始的位置求妹,所以只要知道圓環(huán)即將落到的目標(biāo)位置相對于圓環(huán)開始的位置佳窑,把該坐標(biāo)設(shè)置給dragPos
就可以了父能,css動畫方式就是如此的簡單明了:
<template>
<div class="container">
<div class="column" v-for="(item, cIndex) in columnList">
<div class="ringsBox">
<div
class="ring"
v-for="(ringItem, index) in ringList[item.prop]"
:style="{
<!--省略...-->
transition: transition
}"
></div>
</div>
</div>
</div>
</template>
{
data() {
return {
transition: 'none'
}
},
methods: {
mousedown(e, ringItem, index, prop, columnIndex) {
// ...
// 鼠標(biāo)按下時說明可能要進(jìn)行拖動,那么該屬性要設(shè)為null
this.transition = 'none'
// ...
},
// 重點改造鼠標(biāo)松開事件函數(shù)
async mouseup() {
if (!this.draging) {
return
}
this.draging = false
let columnIndex = this.checkInColumnIndex(this.dragOrder)
let canDraged = this.canDraged(columnIndex, this.dragOrder)
// 設(shè)置過渡效果
this.transition = 'all 0.5s'
if (canDraged) {
// 核心函數(shù)溉委,讓圓環(huán)從松開的位置移動到目標(biāo)位置,因為過渡需要時間坡慌,所以使用await進(jìn)行等待
await this.moveToNewPos(columnIndex, this.dragProp, this.dragIndex)
// 圓環(huán)物理位置過去以后藻三,實際該圓環(huán)的數(shù)據(jù)還是在原來的柱子數(shù)組里的,所以還是需要把它移到目標(biāo)數(shù)組
this.dragToColumn(columnIndex, this.dragProp, this.dragIndex)
// 過渡完以后刪掉過渡效果
this.transition = 'none'
// 復(fù)位數(shù)據(jù)
this.reset()
this.checkPass()
} else {
this.reset()
}
}
}
}
接下來就是要實現(xiàn)上面的移動函數(shù)moveToNewPos
熄求,其實就是計算目標(biāo)位置的坐標(biāo)逗概,該坐標(biāo)是相當(dāng)于圓環(huán)起始坐標(biāo)來說的弟晚,方便計算也先它們都轉(zhuǎn)化為相當(dāng)于瀏覽器窗口,然后相減就得到了最終結(jié)果:
{
moveToNewPos(columnIndex, prop, index) {
// 因為過渡需要500毫秒逾苫,所以使用promise
return new Promise((resolve, rejct) => {
let ring = this.ringList[prop][index]
// 將圓環(huán)起始坐標(biāo)轉(zhuǎn)化為距瀏覽器窗口坐標(biāo)
let startPos = this.getRingPosOffsetWindow(this.dragColumnIndex, ring.order, true)
// 將圓環(huán)目標(biāo)坐標(biāo)轉(zhuǎn)化為距瀏覽器窗口坐標(biāo)
let endPos = this.getRingPosOffsetWindow(columnIndex, ring.order)
// 相減得到目標(biāo)坐標(biāo)相當(dāng)于起始坐標(biāo)的值
this.dragPos.x = endPos.left - startPos.left
this.dragPos.y = endPos.top - startPos.top
// 讓圓環(huán)過渡完
setTimeout(() => {
resolve()
}, 500);
})
}
}
getRingPosOffsetWindow
方法是計算某個柱子上指定索引的圓環(huán)的位置相當(dāng)于瀏覽器窗口的距離,第三個參數(shù)為true
代表該圓環(huán)是否已經(jīng)存在于該柱子铅搓,為false
代表是即將落下的目標(biāo)位置:
{
getRingPosOffsetWindow(columnIndex, order, exist) {
// 該柱子的圓環(huán)數(shù)組
let prop = this.columnList[columnIndex].prop
// 該柱子區(qū)域的尺寸位置信息
let rect = this.$refs['column' + columnIndex][0].getBoundingClientRect()
// 圓環(huán)在該柱子上的索引
let index = this.ringList[prop].length - (exist ? 1 : 0)
// 圓環(huán)相當(dāng)于柱子區(qū)域的位置信息
let left = (100 - (this.wsize - (order - 1) * 10)) / 2 + '%'
let bottom = (this.hsize / this.ringNum) * index + '%'
let height = this.hsize / this.ringNum + '%'
// 轉(zhuǎn)換為像素
let leftPx = rect.width * parseFloat(left) / 100
// 底部線段占了5像素
let _height = rect.height - 5
let topPx = _height - (_height * parseFloat(bottom) / 100) - (parseFloat(height) * _height / 100)
// 轉(zhuǎn)換為屏幕上的坐標(biāo)
let windowLeftPx = rect.left + leftPx
let windowTopPx = rect.top + topPx
return {
left: windowLeftPx,
top: windowTopPx
}
}
}
到這里松開圓環(huán)圓環(huán)就會過渡到目標(biāo)位置狸吞,
最少步數(shù)與自動操作
漢諾塔游戲可以用遞歸來求解,詳細(xì)了解可參考文章開頭提到的文章便斥,此處不再贅述威始,直接貼出遞歸函數(shù):
export default {
data() {
return {
minStepNum: 0//當(dāng)前層數(shù)最少步數(shù)
}
},
methods: {
// 計算指定層數(shù)的解法吉最少步數(shù)
resolveHannuota(num, start, transfer, end) {
if (num <= 0) {
return;
}
this.resolveHannuota(num - 1, start, end, transfer)
console.log(start + '->' + end)
this.minStepNum++
this.resolveHannuota(num - 1, transfer, start, end)
}
}
}
層數(shù)改變很簡單黎棠,把之前寫死的startColRingList
數(shù)組改成遍歷生成就可以了,每次層數(shù)改變后都調(diào)一下上面的resolveHannuota
方法脓斩,minStepNum
累加的結(jié)果就是最少次數(shù)随静,console.log
打印的就是步驟吗讶,三層打印的結(jié)果如下所示:
startColRingList->endColRingList
startColRingList->transferColRingList
endColRingList->transferColRingList
startColRingList->endColRingList
transferColRingList->startColRingList
transferColRingList->endColRingList
startColRingList->endColRingList
可以通過解析該數(shù)據(jù)來實現(xiàn)自動操作恋捆。
// 柱子索引
const propIndex = {
startColRingList: 0,
transferColRingList: 1,
endColRingList: 2,
}
// 自動操作
function auto() {
let index = 0
let loop = async () => {
// autoStepList數(shù)組就是上面console打印的內(nèi)容
if (index > this.autoStepList.length - 1) {
return;
}
let cur = this.autoStepList[index]
let columnIndex = propIndex[cur.to]
this.dragColumnIndex = propIndex[cur.from]
let dragIndex = this.ringList[cur.from].length - 1
this.transition = "all 0.5s";
this.dragOrder = this.ringList[cur.from][dragIndex].order
// 調(diào)用之前過渡的方法
await this.moveToNewPos(columnIndex, cur.from, dragIndex);
// 移動數(shù)組元素
this.dragToColumn(columnIndex, cur.from, dragIndex);
this.transition = "none";
this.dragPos.x = 0
this.dragPos.y = 0
index++
setTimeout(() => {
loop()
}, 500);
}
loop()
}
返回上一步
返回上一步也很簡單沸停,通過數(shù)組記錄下每一步,然后每點一次就把數(shù)組最后一項彈出來爽茴,通過上述動畫方式移動對應(yīng)的圓環(huán)即可绰垂。
首先在之前的mouseup
函數(shù)里保存每一步的操作:
{
// 鼠標(biāo)松開事件函數(shù)
async mouseup() {
// ...
this.transition = 'all 0.5s'
if (canDraged) {
await this.moveToNewPos(columnIndex, this.dragProp, this.dragIndex)
// 在這里把這一步的操作添加到數(shù)組里,注意回退操作是把這一步的目標(biāo)位置回到開始位置
this.historyList.push({
to: this.dragProp,
from: this.columnList[columnIndex].prop
})
// ...
} else {
this.reset()
}
}
}
然后點點擊回退按鈕時彈出最后一步進(jìn)行回退:
{
// 返回上一步
async goback() {
if (this.historyList.length <= 0) {
return
}
let cur = this.historyList.pop()
let columnIndex = propIndex[cur.to]
this.dragColumnIndex = propIndex[cur.from]
let dragIndex = this.ringList[cur.from].length - 1
this.transition = "all 0.5s";
this.dragOrder = this.ringList[cur.from][dragIndex].order
await this.moveToNewPos(columnIndex, cur.from, dragIndex);
this.dragToColumn(columnIndex, cur.from, dragIndex);
this.transition = "none";
this.dragPos.x = 0
this.dragPos.y = 0
}
}
至此胧沫,游戲的全部功能都已完成绒怨,源代碼已經(jīng)上傳到github:https://github.com/wanglin2/hannuota谦疾。