基于Vue3實現(xiàn)數(shù)字華容道游戲的示例代碼
前言
恰逢春之四月,天氣忽熱忽涼,遇游戲大賽,以笨拙之技,書一篇小文。
游戲規(guī)則:存在n*n的格子,需要將它們按數(shù)字順序或圖片順序一一還原即可。
環(huán)境
主要環(huán)境:
vue3 version:3.2.4
vite version:2.5.0
vue-router version:4.0.14
注:這個游戲的路由使用的是自動路由插件
主要插件:
windicss version:3.5.1
運行如圖:
思路
- 搭建環(huán)境,下載依賴
- 運行項目
- 利用windicss主體兼容pc和移動端
姑且認為小于1024的是平板或者手機 lg(1024px)
App.vue
<div class="relative w-full h-full lg:(w-750px h-800px)"> <route-view </div>
主體Game.vue設(shè)置4個主體組件:GameTool.vue游戲工具欄(返回、開始和步數(shù)統(tǒng)計)、GameCnt.vue游戲主體、Tip.vue開局提示、GamePass.vue游戲通過
實現(xiàn)
GameCnt
布局
- 先畫出3*3的格子,這里有多種方法,筆者這里采取最簡單的動態(tài)grid布局實現(xiàn),后來因為css動畫選取的是transform則不用gird布局了
- 寬高獲取,這里要獲取,原因是使用了transform位移動畫,則需要平移距離和寬高了
- 設(shè)置lazyShow,讓第一次渲染不會有transform動畫
- 隱藏最后一個,利用數(shù)組對象value值+css實現(xiàn)
- 添加其他css(windicss不好實現(xiàn)的css)
// 定義行個數(shù) const rowLen = 3 // 定義cnt寬高和item的寬高 const cntWidth = ref(0) const cntHeight = ref(0) const itemWidth = ref(0) const itemHeight = ref(0) // 定義數(shù)組 const lists = ref([]) lists.value = new Array(rowLen.value * rowLen.value).fill(1).map((item, index) => ({ key: index, // 存儲原序號 value: item, // 1 代表不是空位 moveIndex: index })) // 設(shè)置最后一個為-1 lists.value[lists.value.length - 1]['value'] = 0 //獲取dom和渲染 onMounted(() => { // 獲取cnt寬高和item的寬高 getCntWidth() // 讓第一次渲染不會有transform動畫 lazyShow.value = false })
<div v-show="!lazyShow" v-for="(item, index ) in lists" class="box rounded-md overflow-hidden absolute" :class="[item.value ? 'origin' : 'opacity-0']" @click="boxClick(item)" :style="{ transform: `translate(${(item.moveIndex % rowLen) * (1 / rowLen) * cntWidth}px, ${parseInt(item.moveIndex / rowLen) * (1 / rowLen) * cntHeight}px) `, width: itemWidth + 'px', height: itemHeight + 'px' }"> <p class="absolute z-10 text-light-100 left-1/2 top-1/2" :class="hasImg ? 'opacity-60' : ''" :style="{ 'font-size': (180 / rowLen) + 'px' }">{{ item.key + 1 }}</p> </div>
點擊元素的交換
核心代碼:
// 是否在一行 const isInline = parseInt(index / rowLen.value) === parseInt(emptyIndex / rowLen.value) // 在一行是否相鄰 Math.abs(emptyIndex - index) === 1 // 不在一行是否是上下關(guān)系 Math.abs(index - emptyIndex) === rowLen.value
點擊元素上下左右的交換
先判斷是否在一行,再判斷是否相鄰即可。
- 先判斷是否在一行
- 在一行是否相鄰或不在一行是否是上下關(guān)系
- 看情況調(diào)用changeIndex
// 是否在一行 if (isInline) { // 一行則判斷是否左右相鄰 console.log('相差:' + (index - emptyIndex)) // 是否相鄰 if (Math.abs(emptyIndex - index) === 1) { // 改變對應(yīng)moveIndex changeIndex() } else { console.log('不相鄰') return } } else { // 不是則判斷是否上下相鄰 console.log('相差:' + (index - emptyIndex)) // 是否上或者下 if (Math.abs(index - emptyIndex) === rowLen.value) { // 改變對應(yīng)moveIndex changeIndex() } else { console.log('不相鄰') return } } // 聲明改變的數(shù)組moveIndex的方法 const changeIndex = () => { // 步數(shù)改變 emit('stepChange'); // 改變對應(yīng)數(shù)組里的moveIndex 注意不是 index和 moveIndex ([lists.value[item.key].moveIndex, lists.value[emptyItem.key].moveIndex] = [emptyIndex, index]); }
判斷游戲通過
這個地方很簡單,主要判斷數(shù)組對象每一個是否原序號key是否都相等于移動后的moveIndex
// 是否都成功 const getIsAllOk = () => { // return lists.value.some(item => item.moveIndex !== item.key) } // // 判斷是否都成功了 const flag = getIsAllOk() console.log(flag) if (!flag) { // alert('成功') gamePass() }
注:這個gamePass
需要在defineEmits里注冊
打亂數(shù)組
這個地方很有點麻煩,存在無解的情況,暫時沒想到更好的實現(xiàn)方法,這里使用的是排序打亂
// 打亂數(shù)據(jù) const mixData = () => { const arr = new Array(rowLen.value * rowLen.value).fill(0).map((item, index) => index) // console.log(arr) arr.sort(() => { return Math.random() > 0.5 ? -1 : 1 }) arr.forEach((item, index) => { lists.value[index].moveIndex = item }) // 如果直接是成功的則重新來一次排序 const flag = getIsAllOk() if (!flag) { // alert('成功') mixData() } }
注:有想法可以溝通下,一起提升!
動態(tài)格子和寬高
const params = route.query // 定義行個數(shù) rowLen.value = +params.num || 3; // 重新獲取item寬高 cnt.value && getCntWidth()
圖片華容道
圖片使用定位。然后超出截取即可展示出效果
<img v-if="hasImg" class="absolute" :src="Default" alt="" :style="{ width: cntWidth + 'px', maxWidth: cntWidth + 'px', height: cntHeight + 'px', left: -(item.key % rowLen) * (1 / rowLen) * cntWidth + 'px', top: -parseInt(item.key / rowLen) * (1 / rowLen) * cntHeight + 'px' }"> // 是否渲染圖片 hasImg.value = params.hasImg === '1' ? true : false;
GameTool
左側(cè)是返回按鈕,右側(cè)是重新開始按鈕,中間是移動步數(shù)
移動步數(shù)后中間會調(diào)整為移動多少次
注:圖標從iconfont找的
GamePass
- 先布局全屏遮罩
- 設(shè)置中間div樣式
- 設(shè)置通過成功提示和步數(shù)提示即可
GameTip
這個組件就是開局的提示組件
Menu
這個組件就是菜單頁 設(shè)置了兩種入口
最后
GameCnt 可以寫一些測試代碼,方便測試下通關(guān)后的情況
GameCnt 的塊級效果優(yōu)化過,否則只有顏色,不太好看
GamePass 還有優(yōu)化空間,例如圖片通過可以有不同效果啊等等
整體難度不高,可以練習(xí)下vue3,以及游戲思維
到此這篇關(guān)于基于Vue3實現(xiàn)數(shù)字華容道游戲的示例代碼的文章就介紹到這了,更多相關(guān)Vue數(shù)字華容道內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中$nexttick,$set,$forceupdate的區(qū)別
本文主要介紹了vue中$nexttick,$set,$forceupdate的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07vue項目中created()被調(diào)用多次的踩坑實戰(zhàn)
在vue項目中我在created中調(diào)用了兩次get數(shù)據(jù)請求,所以下面這篇文章主要給大家介紹了關(guān)于vue項目中created()被調(diào)用多次的踩坑實戰(zhàn),需要的朋友可以參考下2023-03-03vue中手機號,郵箱正則驗證以及60s發(fā)送驗證碼的實例
下面小編就為大家分享一篇vue中手機號,郵箱正則驗證以及60s發(fā)送驗證碼的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03