基于原生js且非canvas实现的中国象棋(第一版)

70 篇文章 8 订阅
订阅专栏

前言

本人从小就非常喜欢下中国象棋,学习编程后就一直想自己做一个中国象棋的前端游戏,现在终于有“机会”了。
这是第一版的中国象棋,由h5+css3+原生js所实现(非canvas)。
这个版本主要实现的功能包括:棋子的鼠标交互功能,每种棋子的落子规则,将军提示和游戏结束判断,悔棋功能,各种音效等等
注:由于总代码量比较大,所以完整代码我放在github上,大家可以自己去阅读,这里我只挑选核心功能的代码进行讲解。源代码地址: 完整代码
游戏的界面如下图:
在这里插入图片描述

棋盘与棋子

棋盘

棋盘的实现逻辑比较简单,我这里是通过background-image实现的,相关的核心代码如下:

 <div class="board">
            <div class="board-inner">
            </div>
 </div>
.board-inner {
    width: 34rem;
    height: 38rem;
    margin: 0 auto;
    position: relative;
    background-image: url("../images/board.jpg");
    background-size: 100%;
}

棋子

棋子的实现逻辑也不难,每颗棋子我是通过css手动绘制的,css代码如下:

.chess{
    position: absolute;
    cursor: move;
    width: 2.2rem;
    height: 2.2rem;
    line-height: 2.2rem;
    text-align: center;
    border-radius: 50%;
    background-color: wheat;
    font-family: 隶书;
    font-weight: 600;
    font-size: x-large;
    user-select: none;
    transition: margin 0.5s ease;
}
.red{
    color: red;
}

.black{
    color: #000;
}

棋盘与棋子的交互

这部分功能的实现逻辑相对而言是很麻烦,因为每颗棋子都是通过绝对定位定到棋盘上,但是其基本思想是很简单的,通过margin-top以及margin-left来确定二维数组的下标。
定位的核心代码如下:(创建棋子)

 createBoardDom() {
        for (let i = 0; i < this.board.length; i++) {
            const ids = [];
            for (let j = 0; j < this.board[0].length; j++) {
                const chess = this.board[i][j];
                if (chess === role.empty) {
                    ids.push(0);
                    continue;
                }
                const chessDom = document.createElement('div');
                chessDom.classList.add('chess');
                chessDom.classList.add(chess.type);
                chessDom.innerText = chess.text;
                chessDom.setAttribute('id', this.id);
                chessDom.style.marginTop = `${1 + (11.49 * i)}%`;
                chessDom.style.marginLeft = `${0.5 + (11.56 * j)}%`;
                this.boardDom.appendChild(chessDom);
                ids.push(this.id);
                this.id++;
            }
            this.idBoard.push(ids);
        }
        this.historyIds.push(this.deepClone(this.idBoard));
    }

主要的类文件

这个版本主要的js类文件包括board.js(最核心的棋盘类,里面包含棋盘的各种逻辑代码),各种棋子类(车马炮士相兵帅,每个棋子类里面包含了该类别棋子的基本属性以及该类别棋子的操作规则),其它的配置类(比如role.js,text.js,setting.js)。

棋子类

这里我以炮为例子进行讲解,其它种类的棋子,大家可以自己看源码。

import role from './role.js';
export default class Gun {
    constructor(text, type) {
        this.text = text;
        this.type = type; // red ,black
    }

    findRowPositions(rowIndex, colIndex, positions, board) {
        let rowLeft = rowIndex - 1;
        let rowRight = rowIndex + 1;
        // 移动
        while (rowLeft >= 0 && board[rowLeft][colIndex] === role.empty) {
            positions.push([rowLeft, colIndex]);
            rowLeft--;
        }
        while (rowRight < board.length && board[rowRight][colIndex] === role.empty) {
            positions.push([rowRight, colIndex]);
            rowRight++;
        }
        // 吃棋
        if (rowLeft >= 0 && board[rowLeft][colIndex] !== role.empty) {
            rowLeft--;
            while (rowLeft >= 0 && board[rowLeft][colIndex] === role.empty) {
                rowLeft--;
            }
            if (rowLeft >= 0 && board[rowLeft][colIndex].type !== this.type) {
                positions.push([rowLeft, colIndex])
            }
        }
        if (rowRight < board.length && board[rowRight][colIndex] !== role.empty) {
            rowRight++;
            while (rowRight < board.length && board[rowRight][colIndex] === role.empty) {
                rowRight++;
            }
            if (rowRight < board.length && board[rowRight][colIndex].type !== this.type) {
                positions.push([rowRight, colIndex])
            }
        }
    }

    findColPositions(rowIndex, colIndex, positions, board) {
        let colLeft = colIndex - 1;
        let colRight = colIndex + 1;
        // 移动
        while (colLeft >= 0 && board[rowIndex][colLeft] === role.empty) {
            positions.push([rowIndex, colLeft]);
            colLeft--;
        }
        while (colRight < board[rowIndex].length && board[rowIndex][colRight] === role.empty) {
            positions.push([rowIndex, colRight]);
            colRight++;
        }
        // 吃棋
        if (colLeft >= 0 && board[rowIndex][colLeft] !== role.empty) {
            colLeft--;
            while (colLeft >= 0 && board[rowIndex][colLeft] === role.empty) {
                colLeft--;
            }
            if (colLeft >= 0 && board[rowIndex][colLeft].type !== this.type) {
                positions.push([rowIndex, colLeft])
            }
        }
        if (colRight < board[rowIndex].length && board[rowIndex][colRight] !== role.empty) {
            colRight++;
            while (colRight < board[rowIndex].length && board[rowIndex][colRight] === role.empty) {
                colRight++;
            }
            if (colRight < board[rowIndex].length && board[rowIndex][colRight].type !== this.type) {
                positions.push([rowIndex, colRight])
            }
        }
    }

    killRule(position, board) {
        const positions = [];
        const rowIndex = position[0];
        const colIndex = position[1];
        let rowLeft = rowIndex - 1;
        let rowRight = rowIndex + 1;
        // 移动
        while (rowLeft >= 0 && board[rowLeft][colIndex] === role.empty) {
            rowLeft--;
        }
        rowLeft--;
        while (rowRight < board.length && board[rowRight][colIndex] === role.empty) {
            rowRight++;
        }
        rowRight++;
        while (rowLeft >= 0) {
            positions.push([rowLeft, colIndex]);
            rowLeft--;
        }
        while (rowRight < board.length) {
            positions.push([rowRight, colIndex]);
            rowRight++;
        }
        let colLeft = colIndex - 1;
        let colRight = colIndex + 1;
        // 移动
        while (colLeft >= 0 && board[rowIndex][colLeft] === role.empty) {
            colLeft--;
        }
        colLeft--;
        while (colRight < board[rowIndex].length && board[rowIndex][colRight] === role.empty) {
            colRight++;
        }
        colRight++;
        while (colLeft >= 0) {
            positions.push([rowIndex, colLeft]);
            colLeft--;
        }
        while (colRight < board[0].length) {
            positions.push([rowIndex, colRight]);
            colRight++;
        }
        return positions;
    }

    getDisturb(position, targetPosition, board) {
        const positions = [];
        let rowIndex = position[0];
        let colIndex = position[1];
        if (position[1] === targetPosition[1]) {
            if (position[0] > targetPosition[0]) {
                rowIndex--;
                while (board[rowIndex][colIndex] === role.empty) {
                    rowIndex--;
                }
            } else {
                rowIndex++;
                while (board[rowIndex][colIndex] === role.empty) {
                    rowIndex++;
                }
            }
        } else {
            if (position[1] > targetPosition[1]) {
                colIndex--;
                while (board[rowIndex][colIndex] === role.empty) {
                    colIndex--;
                }
            } else {
                colIndex++;
                while (board[rowIndex][colIndex] === role.empty) {
                    colIndex++;
                }
            }
        }
        if (rowIndex === position[0]) {
            if (colIndex < position[1]) {
                for (let j = colIndex + 1; j < position[1]; j++) {
                    positions.push([rowIndex, j]);
                }
            } else {
                for (let j = position[1] + 1; j < colIndex; j++) {
                    positions.push([rowIndex, j]);
                }
            }
        } else {
            if (rowIndex < position[0]) {
                for (let i = rowIndex + 1; i < position[0]; i++) {
                    positions.push([i, colIndex]);
                }
            } else {
                for (let i = position[0] + 1; i < rowIndex; i++) {
                    positions.push([i, colIndex]);
                }
            }
        }
        if (board[rowIndex][colIndex].type === board[position[0]][position[1]].type &&
            board[rowIndex][colIndex].text === board[position[0]][position[1]].text) {

        } else {
            if (rowIndex === targetPosition[0]) {
                if (colIndex < targetPosition[1]) {
                    for (let j = colIndex + 1; j < targetPosition[1]; j++) {
                        positions.push([rowIndex, j]);
                    }
                } else {
                    for (let j = targetPosition[1] + 1; j < colIndex; j++) {
                        positions.push([rowIndex, j]);
                    }
                }
            } else {
                if (rowIndex < targetPosition[0]) {
                    for (let i = rowIndex + 1; i < targetPosition[0]; i++) {
                        positions.push([i, colIndex]);
                    }
                } else {
                    for (let i = targetPosition[0] + 1; i < rowIndex; i++) {
                        positions.push([i, colIndex]);
                    }
                }
            }
        }
        return positions;
    }

    rule(position, board) {
        const positions = [];
        const rowIndex = position[0];
        const colIndex = position[1];
        this.findRowPositions(rowIndex, colIndex, positions, board);
        this.findColPositions(rowIndex, colIndex, positions, board);
        return positions;
    }
}

这个类中有两个属性,分别是type和text,它的作用分别是用于区分红黑方和用于区分红黑方的显示文本。
这个类中有三个主要函数,分别是rule,killRule和getDisturb。rule的参数分别是当前棋子的位置以及当前棋盘的状态,返回当前棋子可以落子的所有位置。killRule和getDisturb两函数是为判断是否为炮绝杀所服务。

棋盘类

基本方法

board类有两个基本的函数,分别是init和destroy。
init里面初始化该类需要的一些属性和方法:

init() {
        this.id = 1;
        this.step = 1;
        this.readyPlay = false;
        this.isBackward = true;
        this.idBoard = [];
        this.startPosition = [];
        this.endPosition = [];
        this.positions = [];
        this.downPositions = [];
        this.historyRecord = [];
        this.historyIds = [];
        this.boardDom = document.getElementsByClassName('board-inner')[0];
        this.domChesses = document.getElementsByClassName('chess');
        this.chessUpSound = document.getElementById('chessUp');
        this.chessDownSound = document.getElementById('chessDown');
        this.warnGeneralSound = document.getElementById('warnGeneral');
        this.surrender = document.getElementById('surrender');
        this.backward = document.getElementById('backward');
        this.createChess();
        this.initBoard();
        this.createBoardDom();
        this.initEvents();
    }

destroy重置棋盘(这里的清除操作可能不够规范):

destroy() {
        this.boardDom.removeEventListener('click', this.clickHHandler);
        this.boardDom.innerHTML = '';
        this.surrender.removeEventListener('click', this.surrenderHandler);
        this.backward.removeEventListener('click', this.backwardHandler);
    }

两个重要的点击事件

除了init和destroy两个基本方法外,还有两个重要的点击事件处理函数,分别是悔棋函数和棋盘点击函数。
悔棋的逻辑比较简单,就直接上代码:

backwardHandler = () => {
        if (!this.isBackward) {
            alert('提子无悔!');
            return;
        }
        if (this.historyIds.length === 1) {
            return;
        }
        const differences = this.getBackward(this.historyRecord[this.historyRecord.length - 2], this.historyRecord[this.historyRecord.length - 1]);
        const start = differences[0];
        const end = differences[1];
        //更新棋盘
        this.historyRecord.pop();
        this.board[start[0]][start[1]] = start[2];
        this.board[end[0]][end[1]] = end[2];
        this.historyIds.pop();
        const arr = this.historyIds[this.historyIds.length - 1];
        this.idBoard[start[0]][start[1]] = arr[start[0]][start[1]];
        this.idBoard[end[0]][end[1]] = arr[end[0]][end[1]];
        //更新dom
        this.renderBoard([start[0], start[1]]);
        if (end[2] !== role.empty) {
            this.recoverBoardDom([end[0], end[1]]);
        }
        this.clearBoard();
        this.step--;
    }

注:这里如果用一个全局index记录当前history的下标,那么就可以实现forward功能。
相比而言,棋盘的点击事件就更复杂一些,先上代码:

clickHandler = (e) => {
        // 点击棋子
        if (e.target != this.boardDom) {
            const playerPosition = this.getChessPosition(e.target.id);
            if (this.invalidPlayer(playerPosition)) {
                return;
            }
            const flag = this.canDown(playerPosition);
            // 已经选中了一个棋子并即将落子
            if (this.readyPlay && flag) {
                this.clearBoard();
                // 需要判断是否是能落子的位置 如果不能落子则清空选择
                this.endPosition = playerPosition;
                this.changeChessPosition();
                this.readyPlay = false;
                this.chessDownSound.play();
                if (this.gameOver()) {
                    setTimeout(() => {
                        alert(`游戏结束!${this.step % 2 === 1 ? '红' : '黑'}方胜`);
                        this.destroy();
                    }, 500);
                    return;
                }
                this.step++;
                this.historyRecord.push(this.deepClone(this.board));
                this.historyIds.push(this.deepClone(this.idBoard));
                this.isBackward = true;
                this.specialHandler(playerPosition);
            } else {
                // 更换选中的棋子
                this.chessUpSound.play();
                if (this.readyPlay && !flag) {
                    this.clearBoard();
                }
                // 如果是不同类
                if (!this.isSimilar(playerPosition)) {
                    return;
                }
                // 如果是同类
                this.startPosition = playerPosition;
                this.setBorder();
                this.readyPlay = true;
                this.isBackward = false;
                // 获取可以落子的位置
                this.showCanDown(playerPosition);
            }
        } else {
            // 点击棋盘
            if (this.readyPlay) {
                this.clearBoard();
                const playerPosition = this.getEmptyPosition(e.offsetX, e.offsetY);
                const flag = this.canDown(playerPosition);
                this.readyPlay = false;
                if (!flag) {
                    return;
                }
                this.endPosition = playerPosition;
                this.changeChessPosition();
                this.step++;
                this.historyRecord.push(this.deepClone(this.board));
                this.historyIds.push(this.deepClone(this.idBoard));
                this.chessDownSound.play();
                this.isBackward = true;
                this.specialHandler(playerPosition);
            }
        }
    }

这个函数将棋盘的点击行为分成了三种情况进行处理:选择空位置选中了一个棋子并即将落子更换选中的棋子。然后根据不同的情况执行不同的功能逻辑,包括的逻辑有落子音效、更新棋盘状态更新dom、更新历史棋盘库、设置棋子的选中特效、清除之前已设置特效的dom以及一些特殊行为的检验(如将军、绝杀、游戏结束)等等。

两个重要的特殊行为

这里有两个重要的特殊行为的函数,分别是将军绝杀
将军的实现逻辑比较简单,就是判断当前玩家落子后,当前玩家的所有棋子是否会在下一步能够触及到对方玩家将军的位置。这里可能会有朋友有疑问,为啥不是直接判断当前这颗棋子是否会在下一步触及到对方玩家将军的位置,如此一来效果也更高。因为这种判断方式会有漏洞,就比如执行跳马后的马后炮将军。
判断将军的代码:

warnGeneral(position) {
        const play = this.board[position[0]][position[1]];
        // 不止一个点,可能有双将军,士相帅可以过滤掉
        const positions = [];
        const allPositions = [];
        const generalPosition = this.getGeneralPosition(play.type);
        for (let i = 0; i < this.board.length; i++) {
            for (let j = 0; j < this.board[0].length; j++) {
                const targetPlay = this.board[i][j];
                if (play.type === role.red) {
                    if (targetPlay.type !== role.red) {
                        continue;
                    }
                    if (targetPlay.text === text.redOfficial || targetPlay.text === text.redPhase || targetPlay.text === text.redGeneral) {
                        continue;
                    }
                } else {
                    if (targetPlay.type !== role.black) {
                        continue;
                    }
                    if (targetPlay.text === text.blackOfficial || targetPlay.text === text.blackPhase || targetPlay.text === text.blackGeneral) {
                        continue;
                    }
                }
                allPositions.push([i, j]);
                const willPositions = targetPlay.rule([i, j], this.board);
                if (this.includes(generalPosition, willPositions)) {
                    positions.push([i, j]);
                }
            }
        }
        return [positions, allPositions];
    }

这里的返回值是准备为判断绝杀所服务,因为绝杀的前提一定得先满足将军。
相比于将军,绝杀的逻辑就显得复杂很多。
这里我给出我验证绝杀的判断逻辑:首先确定将军当前能移动的范围(不包括当前位置),然后判断此范围是否有安全位置,如果没有安全位置,进一步判断当前是否有己方棋子能够干掉造成将军的棋子(只有一枚棋子造成将军),或者是当前是否有己方棋子能够干扰造成将军的所有棋子(且不能形成新的将军,比如双炮将军),如果都不能满足,那么就是绝杀。

absoluteKill(position, allPositions) {
        // 确定将军当前能移动的范围
        const play = this.board[position[0]][position[1]];
        const generalPosition = this.getGeneralPosition(play.type);
        const generalMoves = this.board[generalPosition[0]][generalPosition[1]].rule(generalPosition, this.board);
        // 判断此范围是否有安全位置
        for (let i = 0; i < this.board.length; i++) {
            for (let j = 0; j < this.board[0].length; j++) {
                const chess = this.board[i][j];
                if (chess === this.empty || chess.type !== this.board[position[0]][position[1]].type) {
                    continue;
                }
                const positions = (chess.text === text.redCar || chess.text === text.redGun || chess.text === text.blackCar || chess.text === text.blackGun)
                    ? chess.killRule([i, j], this.board)
                    : chess.rule([i, j], this.board);
                for (let k = 0; k < generalMoves.length; k++) {
                    if (this.includes(generalMoves[k], positions)) {
                        generalMoves.splice(k, 1);
                        k--;
                    }
                }
            }
        }
        if (generalMoves.length > 0) {
            return false;
        }
        // 判断是否能够干掉造成将军的棋子
        if (allPositions.length === 1) {
            const target = allPositions[0];
            for (let i = 0; i < this.board.length; i++) {
                for (let j = 0; j < this.board[0].length; j++) {
                    const chess = this.board[i][j];
                    if (chess === this.empty || chess.type === this.board[target[0]][target[1]].type) {
                        continue;
                    }
                    const positions = chess.rule([i, j], this.board);
                    if (this.includes(target, positions)) {
                        return false;
                    }
                }
            }
        }
        // 找到干扰造成将军的所有棋子的位置(仅限于车马炮)
        const disturbPositions = [];
        for (let i = 0; i < allPositions.length; i++) {
            const chessPosition = allPositions[i];
            const chess = this.board[chessPosition[0]][chessPosition[1]];
            if (chess.text === text.blackCar || chess.text === text.redCar) {
                const positions = chess.getDisturb([chessPosition[0], chessPosition[1]], generalPosition);
                disturbPositions.push([...positions]);
            } else if (chess.text === text.blackGun || chess.text === text.redGun) {
                const positions = chess.getDisturb([chessPosition[0], chessPosition[1]], generalPosition, this.board);
                disturbPositions.push([...positions]);
            } else if (chess.text === text.blackHorse || chess.text === text.redHorse) {
                const positions = chess.getDisturb([chessPosition[0], chessPosition[1]], this.board);
                disturbPositions.push([...positions]);
            }
        }
        let targetPositions = [];
        let doubleKill = false;
        for (let i = 0; i < disturbPositions.length; i++) {
            let flag = false;
            const positions = disturbPositions[i];
            if (positions.length === 0) {
                return true;
            }
            for (let j = 0; j < positions.length; j++) {
                const targetPosition = positions[j];
                if (this.includes(targetPosition, targetPositions)) {
                    doubleKill = true;
                    flag = true;
                    targetPositions = [];
                    break;
                } else {
                    targetPositions.push([...targetPosition]);
                }
            }
            if (flag) {
                break;
            }
        }
        if (doubleKill) {
            const tmpPositions = [];
            for (let i = 0; i < disturbPositions.length; i++) {
                const positions = disturbPositions[i];
                for (let j = 0; j < positions.length; j++) {
                    const targetPosition = positions[j];
                    if (this.includes(targetPosition, tmpPositions)) {
                        targetPositions.push([...targetPosition]);
                    } else {
                        tmpPositions.push([...targetPosition]);
                    }
                }
            }
        }
        // 判断是否能到达这些位置
        for (let i = 0; i < this.board.length; i++) {
            for (let j = 0; j < this.board[0].length; j++) {
                const chess = this.board[i][j];
                if (chess === this.empty || chess.type === this.board[position[0]][position[1]].type) {
                    continue;
                }
                const positions = chess.rule([i, j], this.board);
                for (let k = 0; k < targetPositions.length; k++) {
                    if (this.includes(targetPositions[k], positions)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

其它细节

细心的伙伴不难发现,如果在给创建的棋子dom添加一个css属性pointer-events: none;
那么在点击棋盘的时候就不需要单独去位置一组id数组从而确定被点击棋子的位置。
事实上也确实如此,但是一旦使用的这个属性,那么也就很难甚至无法给棋子赋予一些鼠标特效,比如cursor: move;(如果有大佬有更好的解决方案,欢迎评论区留言)

结语

这是中国象棋第一版的主要内容,具体还有很多细节我在这里就不一一叙述,大家可以在源码中查看。后续第二版会以ai为主而进行扩展,再后面我可能还会将此项目改造成vue以及react形式的项目,毕竟使用框架可以更方便的添加一些配置功能。
最后,大家有什么更好的建议或者代码欢迎评论区留言或者github上留言,下期再会!

js简单做个象棋
好好打代码
01-09 515
【代码】js简单做个象棋。
js实现的单机双人象棋演示及其分析
CrazyTomato的专栏
09-04 2155
http://www.blueidea.com/bbs/NewsDetail.asp?GroupName=Dreamweaver+%26+Javascript%D7%A8%C0%B8&DaysPrune=5&lp=1&id=1723499作者:桃花岛本程序实现的功能为本地二人对弈中国象棋实现语言为javascript+VML,在windows 2000 pro+IE 6sp1的环境下测试
中国象棋-uniapp-单机游戏-(源码).zip
04-10
这是一个uniapp项目源码例子【中国象棋-单机游戏】项目完整,通过HBuilder X开发工具选uniapp方式打开,uniapp项目可编译发行跨端应用(包括了各种小程序以及APP),可编译正常运行,供学习请参考文章https://blog.csdn.net/zs1028/article/details/121152445#comments_18994821
推荐开源项目:VueChessboard - 趣味盎然的Vue.js国际象棋组件
最新发布
gitblog_00046的博客
06-06 397
推荐开源项目:VueChessboard - 趣味盎然的Vue.js国际象棋组件 vue-chessboardChessboard vue component to load positions, create positions and see threats项目地址:https://gitcode.com/gh_mirrors/vu/vue-chessboard 项目介绍 VueChessb...
js象棋实现
03-06
js,css实现象棋功能,有需要的来
js 象棋游戏 _ 支持双方在线对战
jxiao2008的博客
07-09 1132
  说明:实在对不住诸位,的确是我弄错了,views/index.ejs里的对 socket.io.js的引用使用了我本地的绝对路径,需要修改为&lt;script src="/socket.io/socket.io.js"&gt;&lt;/script&gt;即可,对此我重新打了个包。再次歉意!!!   上周做了javascript版的象棋游戏【详见 js中国象棋游戏_应用back...
李小枫原生js象棋
Li_xiaofen的博客
10-13 378
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Do.
基于 HTML、CSS 和 JS中国象棋游戏
07-24
使用技术:HTML+CSS+JS 在线演示地址:https://haiyong.site/moyu/xiangqi/ 说明:中国象棋游戏,点击新手或者初级即可开始游戏,棋盘可放大,手机电脑均可玩,源码完整,代码注释清晰。 使用场景:HTML大作业、个人网站、网页模板、游戏网站等 适用人群:初学者、资深专家均可
原生js基于canvas实现一个简单的前端截图工具代码实例
10-16
本文介绍了一个使用原生JavaScript和HTML5 canvas元素实现的简单前端截图工具的代码实例。这个工具能够让用户在浏览器端截取网页的一部分区域,并以图像形式显示出来。以下是该工具实现的关键技术点: 1. HTML结构...
canvas 实现中国象棋
08-31
在本文中,我们将深入探讨如何使用HTML5的canvas元素来实现一个中国象棋的游戏界面。Canvas是HTML5中用于绘制2D图形的API,通过JavaScript编程可以实现动态、交互式的图形展示。 首先,我们看到HTML结构中有一个id...
中国象棋(原生js+canvas)
12-11
简易的中国象棋 , 由原生JavaScript + canvas 开发, 适合初学者学习jscanvas的应用!
中国象棋棋盘 js
11-03
js制作棋盘,不用图片制作中国象棋棋盘 Jqurey,javascript
js+canvas版象棋博弈之第一步:棋盘绘制
05-26
js+canvas版象棋博弈之第一步:棋盘绘制
JS写的简单中国象棋
06-29
无聊写个简单中国象棋,方便像我这样的菜菜练习象棋技术 ^_^ 功能简单,支持下棋、悔棋和棋盘翻转
使用html+css+JavaScript实现中国象棋的对弈,并给出代码
06-03
实现中国象棋对弈可以通过使用HTML+CSS+JavaScript实现,为了实现完整的功能,需要补充一些实现中国象棋规则的JavaScript代码,比如各种棋子的走法规则等等。仅实现了象棋的基本功能,实际的象棋游戏还需要完善落子规则、吃子规则以及输赢判断等等,实际的象棋游戏需要进一步完善
原生JS使用Canvas实现拖拽式绘图功能
10-16
原生JS使用Canvas实现拖拽式绘图功能的知识点涵盖了Canvas API的基础应用、面向对象编程思想在Canvas绘图中的运用以及鼠标事件的处理,以下是详细解析: 1. Canvas API基础知识 - Canvas元素:HTML5新增的Canvas...
JavaScript画直线、圆、椭圆(不用VML,Canvas) | #hta #javascript #2d
Ghoul To World!
03-10 272
以前计算机图形学的课程设计,算法什么的都是书上Copy的,只不过不用c,而用js而已。 (话说我图形学机试100分,笔试17分,你说杯具不杯具,俺不想研究其理论,只想用它的公式做出我想要的东西而已)。 [img]http://dl.iteye.com/upload/attachment/215381/ef0bff27-fadd-3f09-bdc9-8dd49be908b9.png[/img...
javascript实现中国象棋单机双人
u014339476的专栏
03-25 467
试玩了下,简单实现了人和人对战,没有电脑对战模式,但感觉也够强大了,给大家分享下。 原创不易,转载请注明出处:javascript实现中国象棋单机双人 分享代码下载地址:http://www.zuidaima.com/share/1708009934785536.htm   http://www.huihui.cn/share/34498342   htt
推荐一款强大的JavaScript国际象棋棋盘组件 —— chessboard.js
gitblog_00096的博客
05-10 414
推荐一款强大的JavaScript国际象棋棋盘组件 —— chessboard.js chessboardjsJavaScript chessboard项目地址:https://gitcode.com/gh_mirrors/ch/chessboardjs 在寻求构建与国际象棋相关的互动应用时,一个直观且易于操作的棋盘界面至关重要。今天,我们向您推荐一款名为chessboard.js的开源项目,这...
写文章

热门文章

  • 前端开发工具DevTools的详细知识点总结(一) 44825
  • react框架学习总结(纯干货) 11759
  • Socket编程之聊天室 11618
  • SVR的简单实例应用 8962
  • 一个完整详细的二维SVR案例分析过程 7871

分类专栏

  • 前端 70篇
  • 生活感悟 2篇
  • git 1篇
  • 计算机旅程
  • 机器学习 4篇
  • 资源分享 1篇
  • C# 7篇
  • 数学 1篇
  • 数学思想与计算机编程 6篇
  • 数据结构 2篇
  • 编程算法思想 16篇
  • 小程序 4篇
  • 设计模式 5篇
  • 基本语法 3篇

最新评论

  • 透析极大极小搜索算法和α-β剪枝算法(有案例和完整代码)

    violet__z: 太感谢了!有些文章不仅是抽象,甚至还错了,博主写的真好!

  • SVR的简单实例应用

    pengsiqi_CPU: 大佬,为什么我的数据前面显示没有缺失,出来的cate显示是空集啊?

  • 透析极大极小搜索算法和α-β剪枝算法(有案例和完整代码)

    Lucky Kyte: 博主真是神了,别的文章看得一头雾水,这里非常清晰明了

  • 透析极大极小搜索算法和α-β剪枝算法(有案例和完整代码)

    Z—一: 算法思想的第二张图 min应该是3

  • 透析极大极小搜索算法和α-β剪枝算法(有案例和完整代码)

    BadGrin: 我们老师用与或树来讲的,但是他开讲的时候就模模糊糊地说,只在脑里推算,根本不细讲过程表情包,博主这篇帮大忙了,雀食易懂

大家在看

  • C语言 | Leetcode C语言题解之第423题从英文中重建数字
  • Java | Leetcode Java题解之第424题替换后的最长重复字符
  • Java | Leetcode Java题解之第423题从英文中重建数字
  • Python | Leetcode Python题解之第423题从英文中重建数字
  • C++ | Leetcode C++题解之第423题从英文中重建数字

最新文章

  • 几种前端内联绑定事件的方式
  • 透析极大极小搜索算法和α-β剪枝算法(有案例和完整代码)
  • 多层异步之最优解
2022年3篇
2021年27篇
2020年54篇
2019年40篇

目录

目录

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家泰州玻璃钢花盆厂家武汉公园水景玻璃钢卡通雕塑公司上海艺术商场美陈订购永州衡阳玻璃钢雕塑定制公园玻璃钢雕塑生产厂家大型户外玻璃钢雕塑报价济源玻璃钢卡通雕塑定做厂家湖南玻璃钢动物雕塑价格潮州工艺玻璃钢雕塑河北仿铜玻璃钢雕塑价格镇江春季商场美陈马鞍山小区玻璃钢雕塑生产厂家商场美陈需要会什么南充玻璃钢人物雕塑韶关园林玻璃钢动物雕塑盐城弧形玻璃钢花盆山西玻璃钢不锈钢雕塑小品厂家新密玻璃钢仿铜雕塑厂家商场海洋之歌美陈商场美陈工程清单夏季商场美陈报价兴化玻璃钢雕塑咨询福建玻璃钢仿真水果雕塑山西省玻璃钢雕塑哪里好商场顶部干花美陈布置晋城玻璃钢人物雕塑厂家河源玻璃钢公园雕塑玻璃钢雕塑船造型商场声光电美陈玻璃钢雕塑卡通有哪些公司香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化