利用下午的时间,对这个程序进行了优化,主要拆解成组件形式,大致分为2个部分。1个部分为棋盘主要内容;1个部分为棋盘控制部分。在此过程中不断的了解了:

1、父组件传递子组件值

2、子组件修改父组件值

3、监听某个值的变化,如重新开始restart

4、也对新旧VUE产生了一定的想法,后续在琢磨怎么实现的了,想引入状态管理

【GoBang.vue】:主要布局组件,该组件主要引入上面所说的两个组件,并提取部分公共变量至此

<script>
import MainBoard from './MainBoard.vue'
import OperateBoard from './OperateBoard.vue'

export default {
    data() {
        return {
            player: 1,//1--白棋;2--黑棋
            winner: 0,
            restart: false,
            boardDetail: {
                width: 600,//棋盘大小
                lineNumber: 20,//棋盘线数
                space: 30,//间隙
            }
        }
    },
    components: {
        MainBoard, OperateBoard
    },
    created() {
        document.title = "五子棋";
    },
    watch: {
        restart(nV, oV) {
            if (nV) {
                this.player = 1
                this.winner = 0
                setTimeout(() => {
                    //设置一个1秒延时,用于子组件捕获数据变化
                    this.restart = false
                }, 1000)
            }
        }
    }
}
</script>
<template>
    <div class="root-board">
        <MainBoard :boardDetail="boardDetail"
                   v-model:player="player"
                   v-model:winner="winner"
                   v-model:restart="restart"/>
        <OperateBoard v-model:boardDetail="boardDetail"
                     :player="player"
                     :winner="winner"
                     v-model:restart="restart"/>
    </div>
</template>

<style scoped>
.root-board {
    display: flex;
    justify-content: center;
    align-content: center;
}
</style>

【MainBoard.vue】主要用于下棋的部分,逻辑和之前一样,数据传输形式变了,感觉变复杂了

<script>
export default {
    props: {
        boardDetail: {
            type: Object,
            default: [],
            required: true
        },
        player: {
            type: Number,
            default: 1,
            required: true
        },
        winner: {
            type: Number,
            default: 0,
            required: true
        },
        restart: {
            type: Boolean,
            default: false,
            required: true
        },
    },
    data() {
        return {
            board: [],
        };
    },
    watch: {
        restart(nV, oV) {
            //监听restart重绘命令,以便重新绘制
            if (nV) {
                this.restartGame()
            }
        }
    },
    // 实例创建完成,仅在首次加载时完成
    mounted() {
        let boardDetail = this.boardDetail
        this.drawBoard(boardDetail.width,
            boardDetail.lineNumber,
            boardDetail.space);
    },
    methods: {
        /**
         * 画整体棋盘
         * @param width 所需棋盘整体大小,上下左右预留一半space空间
         * @param lineNumber 线条数,线条数*间距=width
         * @param space 间距
         */
        drawBoard(width, lineNumber, space) {
            const halfSpace = space / 2;

            const canvas = document.getElementById("board");
            const ctx = canvas.getContext("2d");
            // 设置线条颜色
            ctx.strokeStyle = "black";

            for (let i = 0; i < lineNumber; i++) {
                // 绘制横线
                ctx.beginPath();
                ctx.moveTo(halfSpace, i * space + halfSpace);
                ctx.lineTo(width - halfSpace, i * space + halfSpace);
                ctx.stroke();
                // 绘制竖线
                ctx.beginPath();
                ctx.moveTo(i * space + halfSpace, halfSpace);
                ctx.lineTo(i * space + halfSpace, width - halfSpace);
                ctx.stroke();
                //填充数组,重置为0
                this.board.push(new Array(lineNumber).fill(0))
            }
        },
        /**
         * 监听每步棋子,需要传入棋盘信息
         * @param event
         * @param detail 棋盘信息
         */
        handleClickAndDraw(event, detail) {
            //存在输赢以后,不允许在落子
            if (this.winner !== 0) {
                return;
            }
            const lineNumber = detail.lineNumber
            const space = detail.space
            const halfSpace = space / 2;

            // let x = event.offsetX;
            // let y = event.offsetY;
            // console.log(x + '   ' + y)
            // 计算棋子落在哪个方格中
            const cellX = Math.floor((event.offsetX) / space);
            const cellY = Math.floor((event.offsetY) / space);
            // console.log(cellX, cellY)
            // 判断该位置是否有棋子
            if (this.board[cellX][cellY] !== 0) {
                alert("该位置已有棋子")
                return;
            }
            const canvas = document.getElementById("board");
            const ctx = canvas.getContext("2d");
            //画带渐变色的棋子,同心圆形式
            //考虑起点为2,因半径为space一半,避免太大,截止1/3大小
            let grd = ctx.createRadialGradient(
                cellX * space + halfSpace,
                cellY * space + halfSpace,
                2,
                cellX * space + halfSpace,
                cellY * space + halfSpace,
                space / 3
            )
            grd.addColorStop(0, this.player === 1 ? '#FFFFFF' : '#4C4C4C')
            grd.addColorStop(1, this.player === 1 ? '#DADADA' : '#000000')
            ctx.beginPath()
            ctx.fillStyle = grd
            //画圆,半径设置为space/3,同上r1参数一致
            ctx.arc(
                cellX * space + halfSpace,
                cellY * space + halfSpace,
                space / 3,
                0,
                2 * Math.PI,
                false
            );
            ctx.fill();
            ctx.closePath();
            this.board[cellX][cellY] = this.player; //将黑白棋信息存储

            const winner_current = this.checkWinner(this.board, lineNumber) //判断输赢
            if (winner_current !== 0) {
                //代表此次操作有胜负,更新结果
                this.$emit("update:winner", winner_current)
                alert(winner_current)
                return;
            }
            //通知父组件,修改了选手内容值
            this.$emit("update:player", this.player === 1 ? 2 : 1)//交换
        },
        /**
         * 胜负检查
         * @param board X*X 二维数组
         * @param lineNumber 线条数
         * @returns number
         */
        checkWinner(board, lineNumber) {
            // 检查横向是否有五子连线
            for (let i = 0; i < lineNumber; i++) {
                let count = 0;
                for (let j = 0; j < lineNumber; j++) {
                    if (board[i][j] === this.player) {
                        count++;
                    } else {
                        count = 0;
                    }

                    if (count >= 5) return this.player;
                }
            }

            // 检查纵向是否有五子连线
            for (let j = 0; j < lineNumber; j++) {
                let count = 0;
                for (let i = 0; i < lineNumber; i++) {
                    if (board[i][j] === this.player) {
                        count++;
                    } else {
                        count = 0;
                    }

                    if (count >= 5) return this.player;

                }
            }

            // 检查右斜线是否有五子连线
            for (let i = 0; i < lineNumber - 5; i++) {
                for (let j = 0; j < lineNumber - 5; j++) {
                    let count = 0;
                    for (let k = 0; k < 5; k++) {
                        if (board[i + k][j + k] === this.player) {
                            count++;
                        } else {
                            count = 0;
                        }

                        if (count >= 5) return this.player;

                    }
                }
            }

            // 检查左斜线是否有五子连线
            for (let i = 0; i < lineNumber - 5; i++) {
                for (let j = 4; j < lineNumber; j++) {
                    let count = 0;
                    for (let k = 0; k < 5; k++) {
                        if (board[i + k][j - k] === this.player) {
                            count++;
                        } else {
                            count = 0;
                        }

                        if (count >= 5) return this.player;
                    }
                }
            }

            // 如果没有五子连线,则游戏继续
            return 0;
        },
        /**
         * 重置游戏
         */
        restartGame() {
            //清空基础数据
            this.board = []
            //清空画布
            const canvas = document.getElementById("board");
            const ctx = canvas.getContext("2d");
            ctx.clearRect(0, 0, canvas.width, canvas.height)
            //重新绘制
            this.drawBoard(this.boardDetail.width,
                this.boardDetail.lineNumber,
                this.boardDetail.space);
        },
    }
};
</script>

<template>
    <div class="main-board">
        <canvas id="board" class="board-chess" width="600" height="600"
                @click="handleClickAndDraw($event,this.boardDetail)">
        </canvas>
    </div>
</template>

<style scoped>
.main-board {
    padding: 0 0;
    margin: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.board-chess {
    border: 3px solid black;
    padding: 0 0;
    margin: 0 0;
}
</style>

【OperateBoard.vue】主要用于控制棋盘,如重新开始,重新绘制等,主要是通过对父组件的变量修改实现

<script>
export default {
    props: {
        boardDetail: {
            type: Object,
            default: [],
            required: true
        },
        player: {
            type: Number,
            default: 1,
            required: true
        },
        winner: {
            type: Number,
            default: 0,
            required: true
        },
    },
    data() {
        return {
            inputBoardDetail: {
                width: 600,//棋盘大小
                lineNumber: 20,//棋盘线数
                space: 30,//间隙
            }
        };
    },
    mounted() {
        this.inputBoardDetail.width = this.boardDetail.width
        this.inputBoardDetail.lineNumber = this.boardDetail.lineNumber
        this.inputBoardDetail.space = this.boardDetail.space
    },

    methods: {
        changeBoardDetail() {
            const square = this.inputBoardDetail.width
            const lineNumber = this.inputBoardDetail.lineNumber
            //重新设置board大小
            let canvas = document.getElementById("board")
            canvas.width = square
            canvas.height = square
            //重新设置棋盘大小
            this.$emit("update:boardDetail", {
                width: square,
                lineNumber: lineNumber,
                space: square / lineNumber
            })
            //重新绘制游戏
            this.handleRestart()
        },
        handleRestart() {
            this.$emit("update:restart", true)//交换
        }
    }
};
</script>

<template>
    <div class="operate-board">
        <div>
            <span>棋盘信息:</span><br>
            <span>棋盘长宽:<input v-model="this.inputBoardDetail.width"/></span><br>
            <span>棋盘线条数:<input v-model="this.inputBoardDetail.lineNumber"/></span><br>
            <span>棋盘间距:<input v-model="this.inputBoardDetail.space" disabled/></span><br>
            <button @click="changeBoardDetail()">修改
            </button>
        </div>
        <button @click="handleRestart">重新开始</button>
        <div>当前落子:{{ this.player === 1 ? "白" : "黑" }}</div>
        <div>胜利方:{{ this.winner === 1 ? "白棋" : this.winner === 2 ? "黑棋" : "" }}</div>
    </div>
</template>

<style scoped>
.operate-board {
    margin: 0 20px;
}

.board-detail > div input {
    width: 50px;
}
</style>
最后修改:2024 年 11 月 29 日
如果觉得我的文章对你有用,请随意赞赏