canvas扫雷

动态操作宽高需要注意写法,否则画布内容会被清空

一、获取画笔

<template>
<canvas :width="canvasWidth" :height="canvasHeight" @click.stop="leftClick" @contextmenu.prevent.stop="rightClick" id="canvas"></canvas>
</template>
mounted() {
        // 找到canvas节点// 获取画笔
        this.context = document.getElementById('canvas').getContext('2d')
    }

二、模拟棋盘格

1、画矩形

drawBlock(i, j) {
            this.context.fillStyle = '#c0c0c0'
            this.context.fillRect(i * this.blockWidth, j * this.blockWidth, this.blockWidth, this.blockWidth)
            // 画四条线让小方块有立体感
            // 左
            this.drawLine(
                '#ececec',
                i * this.blockWidth + 1,
                j * this.blockWidth + 1,
                i * this.blockWidth + 1,
                j * this.blockWidth + 1 + this.blockWidth - 2
            )
            // 上
            this.drawLine(
                '#ececec',
                i * this.blockWidth + 1,
                j * this.blockWidth + 1,
                i * this.blockWidth + 1 + this.blockWidth - 2,
                j * this.blockWidth + 1
            )
            // 右
            this.drawLine(
                'rgb(128,128,128)',
                i * this.blockWidth + 1 + this.blockWidth - 2,
                j * this.blockWidth + 1,
                i * this.blockWidth + 1 + this.blockWidth - 2,
                j * this.blockWidth + 1 + this.blockWidth - 2
            )
            // 下
            this.drawLine(
                'rgb(128,128,128)',
                i * this.blockWidth + 2,
                j * this.blockWidth + 1 + this.blockWidth - 2,
                i * this.blockWidth + 2 + this.blockWidth,
                j * this.blockWidth + 1 + this.blockWidth - 2
            )
        }

2、画线

drawLine(color, beginX, beginY, endX, endY) {
            this.context.beginPath()
            this.context.strokeStyle = color
            this.context.moveTo(beginX, beginY)
            this.context.lineTo(endX, endY)
            this.context.lineWidth = 2;
            this.context.stroke()
        }

3.循环拼出棋盘格

this.scan = []
for (let i = 0; i < this.canvasWidth / this.blockWidth; i++) {
   this.scan[i] = []
   for (let j = 0; j < this.canvasHeight / this.blockWidth; j++) {
        this.scan[i][j] = {
             isFlag: false,//被右键标记的方块
             isMine: false,//是否是雷
             isTurn: false,//被翻开的方块
         }
         this.drawBlock(i, j)
    }
}

三、右键模拟插旗

rightClick(e) {
            // 获取点击坐标
            let x = parseInt(e.offsetX / this.blockWidth);
            let y = parseInt(e.offsetY / this.blockWidth);
            this.context.beginPath()
            this.context.fillStyle = 'red'
            this.context.moveTo(x * this.blockWidth + this.blockWidth / 2, y * this.blockWidth + 3)
            this.context.lineTo(x * this.blockWidth + this.blockWidth / 2, y * this.blockWidth + this.blockWidth / 2 + 3)
            this.context.lineTo(x * this.blockWidth + this.blockWidth / 4 - 1, y * this.blockWidth + this.blockWidth / 4)
            this.context.fill()
            this.context.beginPath()
            this.context.fillStyle = 'black'
            this.context.moveTo(x * this.blockWidth + this.blockWidth / 2, y * this.blockWidth + this.blockWidth / 2 + 3)
            this.context.lineTo(x * this.blockWidth + this.blockWidth / 4 - 3, y * this.blockWidth + this.blockWidth / 4 * 3 + 3)
            this.context.lineTo(x * this.blockWidth + this.blockWidth / 4 * 3, y * this.blockWidth + this.blockWidth / 4 * 3 + 3)
            this.context.fill()
        }

  四、画圆模拟雷

drawArc(x, y) {
            this.context.beginPath()
            this.context.fillStyle = 'black'
            this.context.arc(x * this.blockWidth + this.blockWidth / 2, y * this.blockWidth + this.blockWidth / 2, this.blockWidth / 4, 0, 2 * Math.PI)
            this.context.fill()
            this.context.stroke()
        }

五、左键模拟翻牌

第一次点击后再随机生成雷,画出翻牌样式,统计翻牌位置周围八个格子是否有雷,有则画出提示文本并结束,没有则记录一下翻牌位置以及其周围八个格子的坐标,再统计这八个格子周围的八个格子是否有雷,没有则这八个格子继续翻牌,继续记录一下被翻牌的位置以及其周围八个格子的坐标,如果坐标不在记录中则递归继续找雷翻牌

leftClick(e) {
            // 获取点击坐标
            let x = parseInt(e.offsetX / this.blockWidth);
            let y = parseInt(e.offsetY / this.blockWidth);
            // 当第一次点击后才生成雷
            if (!this.gameStart) {
                this.gameStart = true
                const column = this.canvasHeight / this.blockWidth
                const row = this.canvasWidth / this.blockWidth
                let list = []
                while (true) {
                    if (list.length === this.mineTotal) {
                        break;
                    }
                    const randomX = Math.floor(Math.random() * (row - 1))
                    const randomY = Math.floor(Math.random() * (column - 1))
                    if (!list.includes(randomX + ',' + randomY) && randomX !== x && randomY !== y) {
                        //排除点击的位置,防止刚点击就失败
                        list.push(randomX + ',' + randomY)
                        this.scan[randomX][randomY].isMine = true
                    }
                }
            }
            // 如果没雷自动翻牌
            if (!this.scan[x][y].isTurn && this.handelBlock(x, y)) {
                this.history = []
                this.findBlock(x, y)
            }
            //如果是雷则显示
            if (this.scan[x][y].isMine && this.gameStart) {
                this.drawArc(x, y)
            }
        }

1.处理翻牌效果以及画文本提示数字

handelBlock(row, column) {
            let block = true;
            if (!this.scan[row][column].isMine) {
                this.scan[row][column].isTurn = true

                // 改背景
                this.context.beginPath()
                this.context.fillStyle = '#fff'
                this.context.fillRect(row * this.blockWidth, column * this.blockWidth, this.blockWidth, this.blockWidth);

                // 画线框
                this.context.beginPath()
                this.context.fillStyle = '#c0c0c0'
                this.context.strokeRect(row * this.blockWidth, column * this.blockWidth, this.blockWidth, this.blockWidth);

                let index = this.getNumber(row, column);
                if (index > 0) {
                    block = false
                    // 画文本雷的个数
                    this.context.beginPath()
                    this.context.fillStyle = this.getNumberColor(index)
                    this.context.font = '27px'
                    this.context.fillText(index, row * this.blockWidth + this.blockWidth / 2 - 5, column * this.blockWidth + this.blockWidth / 2 + 6)
                }
            }
            return block
        }

2.计算翻牌位置周围八个格子雷的个数

getNumber(row, column) {
            let index = 0;
            // 左
            if (row - 1 >= 0 && this.scan[row - 1][column].isMine) {
                index++;
            }
            // 右
            if (row + 1 < this.scan.length && this.scan[row + 1][column].isMine) {
                index++;
            }
            // 上
            if (column - 1 >= 0 && this.scan[row][column - 1].isMine) {
                index++;
            }
            // 下
            if (column + 1 < this.scan[row].length && this.scan[row][column + 1].isMine) {
                index++;
            }
            // 左上
            if (row - 1 >= 0 && column - 1 >= 0 && this.scan[row - 1][column - 1].isMine) {
                index++;
            }
            // 右上
            if (row + 1 < this.scan.length && column - 1 >= 0 && this.scan[row + 1][column - 1].isMine) {
                index++;
            }
            // 左下
            if (row - 1 >= 0 && column + 1 < this.scan[row].length && this.scan[row - 1][column + 1].isMine) {
                index++;
            }
            // 右下
            if (row + 1 < this.scan.length && column + 1 < this.scan[row].length && this.scan[row + 1][column + 1].isMine) {
                index++;
            }
            return index
        }

不同个数提示文本的颜色可以不同

getNumberColor(index) {
            let color = ''
            switch (index) {
                case 7:
                    color = 'red';
                    break;
                case 6:
                    color = 'orange';
                    break;
                case 5:
                    color = 'yellow'
                    break;
                case 4:
                    color = 'green'
                    break;
                case 3:
                    color = 'cyan'
                    break;
                case 2:
                    color = 'blue'
                    break;
                case 1:
                    color = 'purple'
                    break;
            }
            return color
        }

3.记录坐标如果无雷则继续翻牌,如果坐标不在记录中则递归继续翻牌

isExists(x, y) {
            let exist = false
            exist = this.history.includes(x + ',' + y)
            return exist
        },
findBlockTemplate(x, y) {
            this.history.push(x + ',' + y)
            if (!this.scan[x][y].isMine) {
                if (this.handelBlock(x, y)) {
                    this.findBlock(x, y)
                }
            }
        },
findBlock(x, y) {
            this.history.push(x + ',' + y)
            // 左
            if (x - 1 >= 0 && !this.isExists(x - 1, y)) {
                this.findBlockTemplate(x - 1, y)
            }
            // 右
            if (x + 1 < this.scan.length && !this.isExists(x + 1, y)) {
                this.findBlockTemplate(x + 1, y)
            }
            // 上
            if (y - 1 >= 0 && !this.isExists(x, y - 1)) {
                this.findBlockTemplate(x, y - 1)
            }
            // 下
            if (y + 1 < this.scan[x].length && !this.isExists(x, y + 1)) {
                this.findBlockTemplate(x, y + 1)
            }
            // 左上
            if (x - 1 >= 0 && y - 1 >= 0 && !this.isExists(x - 1, y - 1)) {
                this.findBlockTemplate(x - 1, y - 1)
            }
            // 右上
            if (x + 1 < this.scan.length && y - 1 >= 0 && !this.isExists(x + 1, y - 1)) {
                this.findBlockTemplate(x + 1, y - 1)
            }
            // 左下
            if (x - 1 >= 0 && y + 1 < this.scan[x].length && !this.isExists(x - 1, y + 1)) {
                this.findBlockTemplate(x - 1, y + 1)
            }
            // 右下
            if (x + 1 < this.scan.length && y + 1 < this.scan[x].length && !this.isExists(x + 1, y + 1)) {
                this.findBlockTemplate(x + 1, y + 1)
            }
        }

、判断胜利

当所有旗正确标记出所有雷以及翻完所有牌,游戏结束

gameOver() {
            this.realMineTotal = 0 //插旗中雷的个数
            let flagTotal = 0 //插旗的个数
            let turnTotal = 0 //翻牌的个数
            for (let i = 0; i < this.canvasWidth / this.blockWidth; i++) {
                for (let j = 0; j < this.canvasHeight / this.blockWidth; j++) {
                    if (this.scan[i][j].isFlag) {
                        flagTotal++;
                    }
                    if (this.scan[i][j].isMine && this.scan[i][j].isFlag) {
                        this.realMineTotal++;
                    }
                    if (this.scan[i][j].isTurn) {
                        turnTotal++;
                    }
                }
            }
            if (flagTotal === this.mineTotal && this.realMineTotal === this.mineTotal && turnTotal === (this.canvasWidth / this.blockWidth) * (this.canvasHeight / this.blockWidth) - this.realMineTotal) {
                this.gameEnd = true
                this.$confirm('胜利!是否重新开始?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                }).then(() => {
                    this.change()
                })
                    .catch(() => {

                    })
            }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值