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

如何获取经CSS规则缩放后的Canvas鼠标事件坐标?

如何获取CSS缩放后Canvas上的鼠标事件坐标?

当HTML Canvas处于1:1缩放状态(未被CSS放大/缩小)时,获取画布坐标系的鼠标位置很简单:

c2.onpointermove = (e) => {
    var mousemove_coords = { x: e.offsetX, y: e.offsetY };
};

但如果Canvas通过flex+max-width这类CSS规则调整显示尺寸后,直接用上述方法会出现坐标偏差——比如示例中光标本该在绿色方块上方时切换样式,实际却只有移到方块右下角才会变化。

解决方法

核心是计算Canvas的缩放比例,将鼠标在页面中的坐标转换为画布原始坐标系的坐标:

  1. 获取Canvas元素的实际显示区域信息(通过getBoundingClientRect())
  2. 计算横向和纵向的缩放比例:
    • 横向比例 = 画布原始宽度 / 实际显示宽度
    • 纵向比例 = 画布原始高度 / 实际显示高度
  3. 将鼠标相对于页面的坐标,转换为相对于Canvas显示区域的坐标,再乘以对应缩放比例,得到画布坐标系下的正确坐标

修改后的示例代码

JavaScript

var c1 = document.getElementById("canvas1"), ctx1 = c1.getContext("2d");
var c2 = document.getElementById("canvas2"), ctx2 = c2.getContext("2d");
var w = 2000, h = 1000;
var x0 = 50, y0 = 75, x1 = 100, y1 = 200;
ctx1.canvas.width = w;
ctx1.canvas.height = h; 
ctx1.rect(0, 0, w, h);
ctx1.fill();
ctx2.canvas.width = w;
ctx2.canvas.height = h; 
ctx2.rect(x0, y0, x1, y1);
ctx2.fillStyle = "green";
ctx2.fill();

c2.onpointermove = (e) => {
    // 获取Canvas实际显示的位置和尺寸
    const rect = c2.getBoundingClientRect();
    // 计算缩放比例
    const scaleX = c2.width / rect.width;
    const scaleY = c2.height / rect.height;
    // 转换为画布坐标系的坐标
    const canvasX = (e.clientX - rect.left) * scaleX;
    const canvasY = (e.clientY - rect.top) * scaleY;

    var hovered =
        canvasX >= x0 &&
        canvasX <= x1 &&
        canvasY >= y0 &&
        canvasY <= y1;
    c2.style.cursor = hovered ? "move" : "crosshair";
};

CSS

body, .container { height: 100%; width: 100%; margin: 0; padding: 0; }
.container { display: flex; align-items: center; justify-content: center; background-color: yellow; }
.left-column { margin: 1rem; flex: 1; display: flex; align-items: center; justify-content: center; }
.canvas-wrapper { position: relative; }
#canvas1 { max-width: 100%; max-height: 100%; }
#canvas2 { position: absolute; top: 0; left: 0; max-width: 100%; max-height: 100%; }
.right-column { width: 300px; }

HTML

<div class="container">
    <div class="left-column column">
        <div class="canvas-wrapper">
            <canvas id="canvas1"></canvas>
            <canvas id="canvas2"></canvas>
        </div>
    </div>
    <div class="right-column column">
        Hello world
    </div>
</div>

适配实际场景(帧流绘制)

你通过定时请求帧数据绘制到Canvas的场景中,只要画布的原始尺寸(size.w和size.h)在初始化后固定,上述方法同样适用——每次鼠标事件触发时重新计算缩放比例即可,无需额外调整绘制逻辑:

update_frame_task = setInterval(() => {
    fetch("get_frame")
        .then((r) => r.arrayBuffer())
        .then((arr) => {
            if (size === null) {
                size = { w: 2000, h: 1000 };
                ctx.canvas.width = size.w;
                ctx.canvas.height = size.h;
                ctx2.canvas.width = size.w;
                ctx2.canvas.height = size.h;
            }
            var byteArray = new Uint8ClampedArray(arr);
            var imgData = new ImageData(byteArray, size.w, size.h);
            ctx.putImageData(imgData, 0, 0);
        });
}, 200);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:30:54