You need to enable JavaScript to run this app. Bootstrap模态框内HTML5 Canvas获取鼠标坐标存在角落偏移问题
优惠活动
大模型
产品
解决方案
定价
更多

Bootstrap模态框内HTML5 Canvas获取鼠标坐标存在角落偏移问题

提问:Bootstrap模态框内Canvas选区游戏坐标偏移问题

问题描述

在Bootstrap模态框中嵌入HTML5 Canvas元素开发选区游戏,支持用户选中画布内对象进行操作。目前功能在600×600px画布的中心区域运行正常,但越靠近图片角落位置,获取的鼠标坐标偏移越大,且偏移量随点击点与角落的距离缩小而增大。

附带实现代码

JavaScript 代码

document.addEventListener('DOMContentLoaded', (ev) => {
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    canvas.width = 600;
    canvas.height = 600;


    let imgObj = new Image();

    imgObj.onload = function() {
        let w = canvas.width;
        let nw = imgObj.naturalWidth; //1350
        let nh = imgObj.naturalHeight; //900
        console.log(nw);
        console.log(nh);
        let aspect = nw / nh;
        let h = w / aspect;
        console.log('height', h)
        canvas.height = h;
        soldTilesDisplay.textContent = totalTiles-soldTiles;
        ctx.drawImage(imgObj, 0, 0, w, h);
        
        rectangulize_with_IDs();
        fillarraywithrandomTiles(soldTiles);

        //greyscaleOriginal();
        //ctx.drawImage(imgObj, dx, dy);
        //ctx.drawImage(imgObj, dx, dy, dw, dh);
        //ctx.drawImage(imgObj, sx, sy, sw, sh, dx, dy, dw, dh);
    };

    function getMousePosition(canvas, event) {
        let rect = canvas.getBoundingClientRect();
        
        let x = event.clientX - Math.floor(rect.left);
        let y = event.clientY - Math.floor(rect.top); 
        
        storePixelArray(x, y);
        store_rect(x,y);
        clickcounter = clickcounter + 1;
        console.log("Exact Coordinates: X: " + x,
            " Y: " + y, "CLick Counter: "+ clickcounter);
        }

HTML 代码

<div>
    <!-- Bootstrap Modal here!-->
    <div class="row">
        <div class="col-lg-4">

            <!-- Bootstrap Modal here!-->
            <div class="modal fade" id="myModal1">
                <div class="modal-dialog modal-xl">
                    <div class="modal-content">

                        <div class="modal-header">
                            <h3>WIN!</h3>
                        </div>

                        <div class="modal-body">
                            <div class="container-fluid">
                                <div class="row">

                                    <div class="col-md-6">
                                        <canvas id="canvas" style="background-image: url('/images/Berge.png')"></canvas>
                                    </div>

                                    <div class="col-md-6 justify-content-center">

                                        <div class="card text-center" style="width: 30rem;">
                                            <div class="card-body">
                                                <h5 class="card-title" id="Punktestand"> Card Title Score:</h5>
                                                <p class="card-text" id="paragraphImModal">With supporting text below as a natural lead-in to additional content.</p>
                                                <button type="button" class="btn btn-default" id="usePointsPixelButton">Select Pixel first</button>
                                                <br>
                                                <h5 class="selectedPixelAmount" id="AmountOfPixels"> </h5>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div class="modal-footer">
                            <button type="button" class="btn btn-primary btn-lg" data-dismiss="modal">Close</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

问题排查与修复方案

偏移根因

坐标计算逻辑没有适配响应式布局下的Canvas缩放场景:

  1. Canvas被放置在Bootstrap响应式栅格容器中,实际渲染时会被CSS自动调整显示尺寸,canvas.getBoundingClientRect() 返回的是缩放后的视觉边界尺寸,而Canvas的内部绘制坐标系基于代码赋值的canvas.width、canvas.height属性,和视觉显示尺寸并不完全匹配。没有做比例换算时,坐标偏差会随点击位置离中心的距离增大而升高,完全匹配当前的现象。
  2. 代码中对rect.left、rect.top执行Math.floor取整,额外引入固定像素误差,进一步放大偏移。
  3. Canvas标签上单独设置了CSS背景图,CSS背景的缩放规则和Canvas内部的绘制逻辑相互独立,两层渲染的尺寸差异会进一步拉大视觉上的点击偏差。

具体修复步骤

  1. 重写鼠标坐标换算逻辑
    替换原有getMousePosition函数,加入显示尺寸到内部坐标系的比例换算:
    function getMousePosition(canvas, event) {
        const rect = canvas.getBoundingClientRect();
        // 计算内部像素与显示尺寸的缩放比例
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        // 直接换算为Canvas内部坐标系坐标,不对边界值做强制取整
        const x = (event.clientX - rect.left) * scaleX;
        const y = (event.clientY - rect.top) * scaleY;
    
        storePixelArray(x, y);
        store_rect(x, y);
        clickcounter = clickcounter + 1;
        console.log("Exact Coordinates: X: " + x, " Y: " + y, "CLick Counter: "+ clickcounter);
    }
    
  2. 移除Canvas上的内联CSS背景
    删除Canvas标签中的style="background-image: url('/images/Berge.png')"属性,所有图片渲染统一通过ctx.drawImage实现,保证视觉层和绘制层的缩放规则完全一致,避免两层渲染不同步带来的偏差。
  3. 规避Canvas尺寸修改的副作用
    现有代码在图片加载完成后直接修改canvas.height的操作会清空Canvas的全部绘制状态,虽然当前代码紧接着重新执行了drawImage,但如果后续有叠加的绘制逻辑,调整宽高后需要重新补全所有绘制内容,避免内容丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.04 12:45:05