如何获取经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的缩放比例,将鼠标在页面中的坐标转换为画布原始坐标系的坐标:
- 获取Canvas元素的实际显示区域信息(通过
getBoundingClientRect()) - 计算横向和纵向的缩放比例:
- 横向比例 = 画布原始宽度 / 实际显示宽度
- 纵向比例 = 画布原始高度 / 实际显示高度
- 将鼠标相对于页面的坐标,转换为相对于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
相关产品推荐
相关产品推荐

