數獨是源自18世紀瑞士的一種數學遊戲,是一種運用紙、筆進行演算的邏輯遊戲。以下這篇文章主要為大家介紹了關於利用vue開發一個所謂的數獨的相關資料,文中透過範例程式碼介紹的非常詳細,需要的朋友可以參考下。
1.前言
最近工作中遇到一個問題,因為後台管理系統頁面功能暫時沒有新的需求,就在想首頁放什麼東西,最近我想到的就是放個所謂的數獨,為什麼是所謂的數獨,因為規則不同於標準的數獨,只要求每一行每一列數字不一樣就可以了!這個實例也是基於vue的,程式碼分享給大家。給大家程式碼,不是要讓大家直接拷貝程式碼,而是希望能讓大家當做是一個練手的項目,或是學習到知識。如果大家覺得我哪裡寫得不好,寫錯了,歡迎指出,讓大家交流意見,一起進步。
程式碼上傳到github了:有需要的可以star一下! vue-demos
2.運行效果
#3.實作步驟
實現步驟,感覺說得有點繞,建議大家邊寫邊看文章,這樣不會懵。或是直接去看源碼(sudoku),把源碼看懂!這個項目也不複雜!
3-1.準備資料和排版
排版的html css程式碼我不多說了,排版很簡單,這個相信都難不倒大家的。複雜一點的就是數據的互動!
下面開始第一步,把數獨的資料先準備好,資料是什麼,大家都知道,就是像下面這樣的資料!
排版出來的效果就是下面這樣。
html程式碼如下
<p class="num-table" @mouseleave="hoverCol=''" :class="{'shake':isShake}"> <!--遍历每一行--> <p v-for="row,index in allNum" class="num-row chearfix"> <!--遍历行里面的每一列--> <p v-for="num1,indexSub in row" class="num-col"> {{allNumText[index][indexSub]}} </p> </p> </p>
程式碼也很簡單,如下
mounted(){ let arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let row = [], rowCol = 0; for (let i = 0, len = arr1.length; i < len; i++) { row = Object.assign([], arr1); this.allNum.push(row); //删除第一个数字并记录下来 rowCol = arr1.splice(0, 1)[0]; //在最后面插入数字 arr1.push(rowCol) } }
大家也可以發現,這個數據,的每一行和每一列的數字都是不同樣的!
3-2.打亂行
之後就是隨機打亂順序了,打亂順序這個得保證一個前提,就是保證每一行每一列的數字都不一樣。這樣的話,我用了一個簡單粗暴的方法-以行或列為單位,進行打亂。例如,第一行和第三行進行位置交互,第一列和第五列進行位置的交換。下面說下以行為單位的打亂順序!
行的打亂,很簡單,就是隨機打亂數組而已!一行程式碼搞定!
this.allNum.sort((n1, n2) => Math.random() - 0.5);
3-3.打亂列
行打亂了,下面進行以列為單位的打亂,這個稍微複雜一點。
大家想下,例如第二列是第五列的值進行交換,那就是每一行的第二個格子的值和第五個格子的值進行交換,那麼就需要遍歷每一行!來進行交換,至於前面說的第二列和第五列的這個列數,可以用一個函數實現!
下面看程式碼!
//随机获取两列的索引 function randomText() { let rondomIndex = 0, rondomIndexAfter = 0; //获取第一列的索引 rondomIndex = Math.floor(Math.random() * 9); function randomDo() { rondomIndexAfter = Math.floor(Math.random() * 9); //如果第一列和第二列索引一样,第二列的索引再次重新随机获取 if (rondomIndexAfter === rondomIndex) { randomDo(); } } randomDo(); //返回两列的索引 return [rondomIndex, rondomIndexAfter] } //打乱列 let randomArr = [], nowValue = 0; //同样遍历9次 for (let i = 0; i < 9; i++) { randomArr = Object.assign([], randomText()); //遍历每一行,给每一行的随机两列交换值 for (let j = 0, len = this.allNum.length; j < len; j++) { //随机两列交换值 nowValue = this.allNum[j][randomArr[0]]; this.allNum[j][randomArr[0]] = this.allNum[j][randomArr[1]]; this.allNum[j][randomArr[1]] = nowValue; } }
3-3.隨機掏空儲存格
掏空儲存格就是把一些格子隨機設空,然後讓玩數獨的人。把這些單元格給填上!
需求,我現在實現的就是,每一行有把兩個格子設空,這裡我的做法是,把每一個格子的坐標先記錄下來,然後再從記錄的坐標裡面隨機獲取坐標,用取得到的座標,進行設空!
首先,要取得所有點的座標
//记录所有坐标 let rowText = '', arrText = [] for (let i = 0; i < 9; i++) { rowText = '' for (let j = 0; j < 9; j++) { rowText += i + '-' + j + ','; } arrText.push(rowText.substr(0, rowText.length - 1)) } console.log(arrText);
#看到這個座標,大家很容易的知道,陣列的一個元素,就是第一行, '0-0'就是第一行第一個格子。數組最後一個元素,就是最後一行,『8-8'就是最後一行,最後一個格子,其他如此類推!
下面進行隨機掏空,程式碼也很簡單!
//随机掏空 let nowItme = [], _option, nowOption = []; for (let i = 0; i < 9; i++) { //抽取当前行的所有坐标 nowItme = arrText[i].split(','); nowOption = []; //当前行的随机两个坐标掏空 for (let j = 0; j < 2; j++) { //抽取当前行的随机一个坐标 _option = Math.floor(Math.random() * nowItme.length); //分割坐标的x,y nowOption = nowItme.splice(_option,1)[0].split("-"); this.allNum[nowOption[0]][nowOption[1]] = ''; } }
這樣相信大家都覺得奇怪,下面進行下樣式的該寫,就是把設空了的格子的樣式改一下! .no這個class對應的樣式我在css那裡寫好了,大家注意下。
<!--遍历每一行--> <p v-for="row,index in allNum" class="num-row chearfix"> <!--遍历行里面的每一列--> <!-- no:被掏空数组的样式 --> <p v-for="num1,indexSub in row" :class="{'no':num1===''}" class="num-col"> {{allNumText[index][indexSub]}} </p> </p>#
3-4.显示数字键盘
首先,我简单的用一个流程图说下逻辑,如下
然后关于数字键盘的位置,看下图(数字键盘的样式我不多说了,就是一个是相对定位,一个绝对定位的设置而已)
如上图,我点击的是第一行第三个格子,首先,我期待被点击的格子的样式有所改变,方便我区分,这个不难,用一个class改变样式就可以了,这个可以看下面的代码,我用一个.cur的class控制样式。还有一个就是期待数字键盘在第二行,第四个格子那里出现。这样的话,大家就知道,数字键盘的位置是怎么定位的了!数字键盘的top就是,被点击格子所在的行的索引+160(60是格子的宽高),left就是,被点击格子所在的列的索引+160(60是格子的宽高)。比如上图,第一行第三个格子,top=(0+1)*60+'px',left=(2+1)*60+'px'。
代码如下
<!--遍历每一行--> <p v-for="row,index in allNum" class="num-row chearfix"> <!--遍历行里面的每一列--> <!-- no:被掏空数组的样式 cur:格子被点击时触发,被点击的格子样式 --> <p v-for="num1,indexSub in row" :class="{'no':num1==='', 'cur':curRow===index&&indexSub===curCol}" @click="showCheck(index,indexSub)" class="num-col"> {{allNumText[index][indexSub]}} </p> </p> <!--数字键盘--> <p class="num-check chearfix" :style="{'top':(curRow+1)*60+'px','left':(curCol+1)*60+'px'}" v-show="checkShow"> <ul> <li @click="inputText(1)">1</li> <li @click="inputText(2)">2</li> <li @click="inputText(3)">3</li> <li @click="inputText(4)">4</li> <li @click="inputText(5)">5</li> <li @click="inputText(6)">6</li> <li @click="inputText(7)">7</li> <li @click="inputText(8)">8</li> <li @click="inputText(9)">9</li> </ul> </p>
js代码
/** * @description 显示数字键盘 * @param i1 * @param i2 */ showCheck(i1, i2){ //点击的格子是否是被掏空的格子 if (this.allNum[i1][i2] !== '') { return } //点击的格子如果是上一次点击的格子(当前格子) if (i1 === this.curRow && i2 === this.curCol) { //隐藏数字键盘,curRow和curCol设空 this.checkShow = false; this.curRow = ''; this.curCol = ''; } else { //隐藏数字键盘,curRow和curCol分别设置成当前的点 this.checkShow = true; this.curRow = i1; this.curCol = i2; } },
运行效果
3-5.高亮显示同行同列
这一步很简单,首先,高亮显示行,大家都知道怎么做了,就是行对应的p,设置一个:hover,然后对应设置单元格的样式而已!这个不多说!
然后,高亮显示列,复杂一点,但是也很简单,原理我想大家也知道,就是当鼠标进如格子的时候,在data里面,用一个变量储存进入的格子的列的索引,然后加上判断,如果格子的列的索引等于进入的格子的列的索引。就加上一个class,这里我用.cur-col。
代码如下
<!--遍历每一行--> <p v-for="row,index in allNum" class="num-row clear"> <!--遍历行里面的每一列--> <!-- no:被掏空数组的样式 cur:格子被点击时触发,被点击的格子样式 cur-col:鼠标进入的时候触发,和被点击格子同一列的格子的样式 --> <p v-for="num1,indexSub in row" :class="{'no':num1==='', 'cur':curRow===index&&indexSub===curCol, 'cur-col':hoverCol===indexSub}" @click="showCheck(index,indexSub)" @mouseenter="hoverCol=indexSub;" class="num-col"> {{allNumText[index][indexSub]}} </p> </p>
运行效果
3-6.填写操作和错误提示
这一步的操作函数,我直接发代码吧,看代码比我说的会清晰些,毕竟说的有点绕
<!--遍历每一行--> <p v-for="row,index in allNum" class="num-row clear"> <!--遍历行里面的每一列--> <!-- no:被掏空数组的样式 cur:格子被点击时触发,被点击的格子样式 cur-col:鼠标进入的时候触发,和被点击格子同一列的格子的样式 err:填写错误的时候触发的样式 --> <p v-for="num1,indexSub in row" :class="{'no':num1==='', 'cur':curRow===index&&indexSub===curCol, 'cur-col':hoverCol===indexSub, 'err':(optionNow.x===index&&optionNow.y===indexSub)||(optionNowInRow.x===index&&optionNowInRow.y===indexSub)||(optionNowInCol.x===index&&optionNowInCol.y===indexSub)}" @click="showCheck(index,indexSub)" @mouseenter="hoverCol=indexSub;" class="num-col"> {{allNumText[index][indexSub]}} </p> </p>
js代码
inputText(_text){ //*****************************检查前的初始化 let _row = this.curRow, _col = this.curCol; this.curRow = ''; this.curCol = ''; this.isErr = false; this.optionNow = { x: '', y: '', } this.optionNowInRow = { x: '', y: '', } this.optionNowInCol = { x: '', y: '', } //*****************************检查行 //根据当前格子进行赋值 this.allNumText[_row][_col] = _text; let rowCheck = Object.assign(this.allNumText[_row], []); this.checkShow = false; for (let i = 0, len = rowCheck.length; i < len; i++) { //如果值一样,但是坐标不一样,就是填写错误 if (_text === rowCheck[i] && _col !== i) { this.isErr = true; this.isShake = true; //记录当前格子的信息 this.optionNow = { x: _row, y: _col, } //记录和当前格子同一行,以及同一个值的格子的坐标 this.optionNowInRow = { x: _row, y: i, } } } //*****************************检查列 let colCheck = []; //首先把每一行的那一列的数值保存起来 for (let i = 0, len = this.allNumText.length; i < len; i++) { colCheck.push(this.allNumText[i][_col]); } //遍历检查 for (let i = 0, len = colCheck.length; i < len; i++) { //如果值一样,但是坐标不一样,就是填写错误 if (_text === colCheck[i] && _row !== i) { this.isErr = true; this.isShake = true; //记录和当前格子同一列,以及同一个值的格子的坐标 this.optionNowInCol = { x: i, y: _col, } } } //如果发现的同样的 if (this.isErr) { setTimeout(() => { this.isShake = false; }, 1000) return; } //如果数组去重后,长度小于9,就是行没完成 rowCheck = rowCheck.filter(item => item !== ''); if (rowCheck.length !== 9) { //console.log('行没完成') return; } let coloCheck = []; //如果数组去重后,长度小于9,就是列没完成 for (let i = 0, len = this.allNumText.length; i < len; i++) { coloCheck = [...new Set(this.allNumText[i])]; coloCheck = coloCheck.filter(item => item !== ''); if (coloCheck.length !== 9) { //console.log('没完成') return; } } alert('挑战成功,但是没奖品'); this.numShow = false; }
上面的代码逻辑,简单说下
1..err 这个class是设置红色字体所使用的,至于判断,就是在inputText这个函数里面,有optionNow和 optionNowInRow和optionNowInCol。只要格子的坐标等于三者其中之一,就会添加这个class,就会变红。
2..isShake这个class是控制,抖动的动画,添加上了之后,在一秒后,要去掉这个class,不然下次添加没有动画效果。
3.在inputText这个函数里面,我操作的数独列表,并不是之前,提到的allNum,而是利用allNum,深度拷贝生成出的allNumText(this.allNumText = JSON.parse(JSON.stringify(this.allNum));)
。主要就是为了避免下图的情况!
这样是为了往掏空的格子输入数字的时候,然后那个格子就不能再改了,即使是填错了,都不能改。样式控制也不正确!正确的格式应该是下面这样,即使填入了,格子的样式还是灰色的,这样可以方便的知道哪个格子是当时被掏空的,填写错了,也是可以改的。
4.完整代码
vue-所谓的数独
所谓的数独:规则
1.每一行数字不重复
2.每一列数字不重复
{{allNumText[index][indexSub]}}
reset.css和vue.min.js大家自行到github下载!
5.小结
好了,用vue做的所谓的数独,就写到这里了,主要就是逻辑有点绕,其它的问题相信都难不倒大家。这个实例比之前快速入门的三个小实例要麻烦一点,但是也很好理解!大家只要稍微看下估计都不难理解!最后,如果大家觉得文章写得不好,哪里写错了,欢迎给建议或者指点下迷津。期待和大家交流意见,共同进步!
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上是使用vue如何開發數獨遊戲的詳細內容。更多資訊請關注PHP中文網其他相關文章!