You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript游戏开发中canvas无法按100ms间隔定时刷新如何解决?

问题根因

浏览器的JavaScript执行和页面渲染运行在同一个主线程上,同步JS代码执行时会完全阻塞后续的渲染任务。你在mainLoop里先执行board.showBoard(ctx)把绘制指令写入canvas缓冲区后,马上执行同步的深度树计算逻辑,浏览器必须等这个耗时数秒的计算任务跑完,才会把缓冲区的内容渲染到屏幕上,所以看起来玩家和电脑的落子是同时更新的。

解决方案

方案1:用Web Worker运行AI计算(最推荐)

把耗时的深度树计算逻辑独立放到Web Worker文件中,计算完全在后台线程运行,不会阻塞主线程的渲染和交互。
修改后的主线程代码示例:

function playGame() {
    // 初始化AI计算专属Worker
    const aiWorker = new Worker('ai-calculator.js');
    let isAiCalculating = false; // 标记AI计算状态,避免重复触发

    function doKeyDown(evt) {
        console.log(evt.keyCode)
        // 只有玩家回合、且AI没有在计算时才响应玩家操作
        if (evt.keyCode > 48 && evt.keyCode < 58 && game.currentTurn === 'player' && !isAiCalculating) { 
            game.makeMove(evt.keyCode-48);
        }
    }

    function mainLoop(ctx, game, board) {
        board.showBoard(ctx); // 每次循环优先刷新界面
        // 仅在电脑回合、且AI未计算时触发走棋逻辑
        if (game.currentTurn === 'computer' && !isAiCalculating) {
            isAiCalculating = true;
            // 把当前游戏状态发送给Worker计算
            aiWorker.postMessage(game.getStatus());
        }
    }

    // 监听Worker返回的计算结果
    aiWorker.onmessage = (e) => {
        const aiMove = e.data;
        game.makeMove(aiMove);
        isAiCalculating = false;
    }

    Canvas.canvas = document.getElementById("myCanvas");
    let game = new Game();
    let board = new Board(game);
    window.addEventListener( "keydown", doKeyDown, true);
    Canvas.ctx = Canvas.canvas.getContext('2d');
    setInterval(() => {mainLoop(Canvas.ctx, game, board)}, 100);
};  

document.addEventListener('DOMContentLoaded', playGame);

你只需要把深度树计算逻辑放到单独的ai-calculator.js文件中,收到主线程传来的游戏状态后计算走法,再通过postMessage把结果返回主线程即可。

方案2:给渲染让出执行间隙(不用Worker的折中方案)

如果AI计算逻辑不好拆分到Worker中,可以在触发AI计算前主动让出线程,让浏览器先完成当前帧的渲染:

function mainLoop(ctx, game, board) {
    board.showBoard(ctx);
    if (game.currentTurn === 'computer' && !isAiCalculating) {
        isAiCalculating = true;
        // 等待当前帧渲染完成后再执行AI计算
        requestAnimationFrame(() => {
            setTimeout(() => {
                game.makeMove(0);
                isAiCalculating = false;
            }, 0)
        })
    }
}

这个方案的缺点是AI计算的几秒仍然会卡住页面交互,仅能保证玩家落子先渲染出来。

额外优化提示

你原代码中mainLoop每100ms会无差别调用game.makeMove(0),没有判断回合状态,会导致电脑重复走棋,建议给Game类增加回合状态标记,只有轮到对应角色时才触发走棋逻辑。

内容的提问来源于stack exchange,提问作者Nic Oatridge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 13:15:04