基于Vue.js實現(xiàn)數(shù)字拼圖游戲
先來看看效果圖:
功能分析
當然玩歸玩,作為一名Vue愛好者,我們理應深入游戲內(nèi)部,一探代碼的實現(xiàn)。接下來我們就先來分析一下要完成這樣的一個游戲,主要需要實現(xiàn)哪些功能。下面我就直接將此實例的功能點羅列在下了:
1.隨機生成1~15的數(shù)字格子,每一個數(shù)字都必須出現(xiàn)且僅出現(xiàn)一次
2.點擊一個數(shù)字方塊后,如其上下左右有一處為空,則兩者交換位置
3.格子每移動一步,我們都需要校驗其是否闖關成功
4.點擊重置游戲按鈕后需對拼圖進行重新排序
以上便是本實例的主要功能點,可見游戲功能并不復雜,我們只需一個個攻破就OK了,接下來我就來展示一下各個功能點的Vue代碼。
構建游戲面板
作為一款以數(shù)據(jù)驅動的JS框架,Vue的HTML模板很多時候都應該綁定數(shù)據(jù)的,比如此游戲的方塊格子,我們這里肯定是不能寫死的,代碼如下:
<template> <div class="box"> <ul class="puzzle-wrap"> <li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle" ></li> </ul> </div> </template> <script> export default { data () { return { puzzles: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] } } } </script>
這里我省略了css樣式部分,大家可以先不用關心。以上代碼我們將1~15的數(shù)字寫死在了一個數(shù)組中,這顯然不是隨機排序的,那么我們就來實現(xiàn)隨機排序的功能。
隨機排序數(shù)字
<template> <div class="box"> <ul class="puzzle-wrap"> <li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle" ></li> </ul> </div> </template> <script> export default { data () { return { puzzles: [] } }, methods: { // 重置渲染 render () { let puzzleArr = [], i = 1 // 生成包含1 ~ 15數(shù)字的數(shù)組 for (i; i < 16; i++) { puzzleArr.push(i) } // 隨機打亂數(shù)組 puzzleArr = puzzleArr.sort(() => { return Math.random() - 0.5 }); // 頁面顯示 this.puzzles = puzzleArr this.puzzles.push('') }, }, ready () { this.render() } }
以上代碼,我們利用for循環(huán)生成了一個1~15的有序數(shù)組,之后我們又利用原生JS的sort方法隨機打亂數(shù)字,這里還包含了一個知識點就是Math.random()
方法。
利用sort()
方法進行自定義排序,我們需要提供一個比較函數(shù),然后返回一個用于說明這兩個值的相對順序的數(shù)字,其返回值如下:
1.返回一個小于 0 的值,說明 a 小于 b
2.返回 0,說明 a 等于 b
3.返回一個大于 0 的值,說明 a 大于 b
這里利用Math.random()
生成一個 0 ~ 1 之間的隨機數(shù),再減去0.5,這樣就會有一半概率返回一個小于 0 的值, 一半概率返回一個大于 0 的值,就保證了生成數(shù)組的隨機性,實現(xiàn)了動態(tài)隨機生成數(shù)字格子的功能。
需要注意的是,我們還在數(shù)組最后插了一個空字符串,用來生成唯一的空白格子。
交換方塊位置
<template> <div class="box"> <ul class="puzzle-wrap"> <li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle" @click="moveFn($index)" ></li> </ul> </div> </template> <script> export default { data () { return { puzzles: [] } }, methods: { // 重置渲染 render () { let puzzleArr = [], i = 1 // 生成包含1 ~ 15數(shù)字的數(shù)組 for (i; i < 16; i++) { puzzleArr.push(i) } // 隨機打亂數(shù)組 puzzleArr = puzzleArr.sort(() => { return Math.random() - 0.5 }); // 頁面顯示 this.puzzles = puzzleArr this.puzzles.push('') }, // 點擊方塊 moveFn (index) { // 獲取點擊位置及其上下左右的值 let curNum = this.puzzles[index], leftNum = this.puzzles[index - 1], rightNum = this.puzzles[index + 1], topNum = this.puzzles[index - 4], bottomNum = this.puzzles[index + 4] // 和為空的位置交換數(shù)值 if (leftNum === '') { this.puzzles.$set(index - 1, curNum) this.puzzles.$set(index, '') } else if (rightNum === '') { this.puzzles.$set(index + 1, curNum) this.puzzles.$set(index, '') } else if (topNum === '') { this.puzzles.$set(index - 4, curNum) this.puzzles.$set(index, '') } else if (bottomNum === '') { this.puzzles.$set(index + 4, curNum) this.puzzles.$set(index, '') } } }, ready () { this.render() } } </script>
1.這里我們首先在每個格子的li上添加了點擊事件@click="moveFn($index)",通過$index參數(shù)獲取點擊方塊在數(shù)組中的位置
2.其次獲取其上下左右的數(shù)字在數(shù)組中的index值依次為index - 4、index + 4、index - 1、index + 1
3.當我們找到上下左右有一處為空的時候我們將空的位置賦值上當前點擊格子的數(shù)字,將當前點擊的位置置為空
備注:我們?yōu)槭裁匆褂?set方法,而不直接用等號賦值呢,這里包含了Vue響應式原理的知識點。
// 因為 JavaScript 的限制,Vue.js 不能檢測到下面數(shù)組變化: // 1.直接用索引設置元素,如 vm.items[0] = {}; // 2.修改數(shù)據(jù)的長度,如 vm.items.length = 0。 // 為了解決問題 (1),Vue.js 擴展了觀察數(shù)組,為它添加了一個 $set() 方法: // 與 `example1.items[0] = ...` 相同,但是能觸發(fā)視圖更新 example1.items.$set(0, { childMsg: 'Changed!'})
檢測是否闖關成功
<template> <div class="box"> <ul class="puzzle-wrap"> <li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle" @click="moveFn($index)" ></li> </ul> </div> </template> <script> export default { data () { return { puzzles: [] } }, methods: { // 重置渲染 render () { let puzzleArr = [], i = 1 // 生成包含1 ~ 15數(shù)字的數(shù)組 for (i; i < 16; i++) { puzzleArr.push(i) } // 隨機打亂數(shù)組 puzzleArr = puzzleArr.sort(() => { return Math.random() - 0.5 }); // 頁面顯示 this.puzzles = puzzleArr this.puzzles.push('') }, // 點擊方塊 moveFn (index) { // 獲取點擊位置及其上下左右的值 let curNum = this.puzzles[index], leftNum = this.puzzles[index - 1], rightNum = this.puzzles[index + 1], topNum = this.puzzles[index - 4], bottomNum = this.puzzles[index + 4] // 和為空的位置交換數(shù)值 if (leftNum === '') { this.puzzles.$set(index - 1, curNum) this.puzzles.$set(index, '') } else if (rightNum === '') { this.puzzles.$set(index + 1, curNum) this.puzzles.$set(index, '') } else if (topNum === '') { this.puzzles.$set(index - 4, curNum) this.puzzles.$set(index, '') } else if (bottomNum === '') { this.puzzles.$set(index + 4, curNum) this.puzzles.$set(index, '') } this.passFn() }, // 校驗是否過關 passFn () { if (this.puzzles[15] === '') { const newPuzzles = this.puzzles.slice(0, 15) const isPass = newPuzzles.every((e, i) => e === i + 1) if (isPass) { alert ('恭喜,闖關成功!') } } } }, ready () { this.render() } } </script>
我們在moveFn
方法里調(diào)用了passFn
方法來進行檢測,而passFn
方法里又涉及了兩個知識點:
(1)slice方法
通過slice方法我們截取數(shù)組的前15個元素生成一個新的數(shù)組,當然前提了數(shù)組隨后一個元素為空
(2)every方法
通過every方法我們來循環(huán)截取后數(shù)組的每一個元素是否等于其index+1值,如果全部等于則返回true,只要有一個不等于則返回false
如果闖關成功那么isPass的值為true,就會alert "恭喜,闖關成功!"提示窗,如果沒有則不提示。
重置游戲
重置游戲其實很簡單,只需添加重置按鈕并在其上調(diào)用render
方法就行了:
<template> <div class="box"> <ul class="puzzle-wrap"> <li :class="{'puzzle': true, 'puzzle-empty': !puzzle}" v-for="puzzle in puzzles" v-text="puzzle" @click="moveFn($index)" ></li> </ul> <button class="btn btn-warning btn-block btn-reset" @click="render">重置游戲</button> </div> </template> <script> export default { data () { return { puzzles: [] } }, methods: { // 重置渲染 render () { let puzzleArr = [], i = 1 // 生成包含1 ~ 15數(shù)字的數(shù)組 for (i; i < 16; i++) { puzzleArr.push(i) } // 隨機打亂數(shù)組 puzzleArr = puzzleArr.sort(() => { return Math.random() - 0.5 }); // 頁面顯示 this.puzzles = puzzleArr this.puzzles.push('') }, // 點擊方塊 moveFn (index) { // 獲取點擊位置及其上下左右的值 let curNum = this.puzzles[index], leftNum = this.puzzles[index - 1], rightNum = this.puzzles[index + 1], topNum = this.puzzles[index - 4], bottomNum = this.puzzles[index + 4] // 和為空的位置交換數(shù)值 if (leftNum === '') { this.puzzles.$set(index - 1, curNum) this.puzzles.$set(index, '') } else if (rightNum === '') { this.puzzles.$set(index + 1, curNum) this.puzzles.$set(index, '') } else if (topNum === '') { this.puzzles.$set(index - 4, curNum) this.puzzles.$set(index, '') } else if (bottomNum === '') { this.puzzles.$set(index + 4, curNum) this.puzzles.$set(index, '') } this.passFn() }, // 校驗是否過關 passFn () { if (this.puzzles[15] === '') { const newPuzzles = this.puzzles.slice(0, 15) const isPass = newPuzzles.every((e, i) => e === i + 1) if (isPass) { alert ('恭喜,闖關成功!') } } } }, ready () { this.render() } } </script> <style> @import url('./assets/css/bootstrap.min.css'); body { font-family: Arial, "Microsoft YaHei"; } .box { width: 400px; margin: 50px auto 0; } .puzzle-wrap { width: 400px; height: 400px; margin-bottom: 40px; padding: 0; background: #ccc; list-style: none; } .puzzle { float: left; width: 100px; height: 100px; font-size: 20px; background: #f90; text-align: center; line-height: 100px; border: 1px solid #ccc; box-shadow: 1px 1px 4px; text-shadow: 1px 1px 1px #B9B4B4; cursor: pointer; } .puzzle-empty { background: #ccc; box-shadow: inset 2px 2px 18px; } .btn-reset { box-shadow: inset 2px 2px 18px; } </style>
這里我一并加上了css代碼。
總結
以上就是本文的全部內(nèi)容,其實本游戲的代碼量不多,功能點也不是很復雜,不過通過Vue來寫這樣的游戲,有助于我們了解Vue以數(shù)據(jù)驅動的響應式原理,在簡化代碼量的同時也增加了代碼的可讀性。希望本文對大家學些Vue有所幫助。
相關文章
vue3-print-nb實現(xiàn)頁面打印(含分頁打印)示例代碼
大多數(shù)后臺系統(tǒng)中都存在打印的需求,在有打印需求時,對前端來說當然是直接打印頁面更容易,下面這篇文章主要給大家介紹了關于vue3-print-nb實現(xiàn)頁面打印(含分頁打印)的相關資料,需要的朋友可以參考下2024-01-01關于element-ui?select?下拉框位置錯亂問題解決
這篇文章主要介紹了關于element-ui?select?下拉框位置錯亂問題解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09