Canvas坐标系与鼠标位置缩放不一致问题求助
解决Canvas绘图位置与鼠标坐标不匹配的问题
按下Q键绘制正方形时,图形位置与鼠标位置偏差明显,离屏幕原点越远错位越严重,甚至超出视口。
问题根源
Canvas元素存在两个独立尺寸:
- CSS样式尺寸:控制canvas在页面上的显示大小(当前通过
100vw/100vh设为全屏) - 绘图缓冲区尺寸:canvas内部的像素画布尺寸,默认是300px×150px
当CSS显示尺寸和绘图缓冲区尺寸不一致时,浏览器会拉伸/压缩绘图内容,导致绘图坐标(基于缓冲区)和屏幕鼠标坐标(基于显示尺寸)不对应,距离原点越远,缩放比例带来的偏差越大。
修复步骤及代码修改
1. 初始化Canvas的实际绘图尺寸
在main.js开头,将canvas的width和height属性设置为窗口的实际像素值,替代仅依赖CSS的方式:
const canvas = document.getElementById("main-canvas"); // 设置绘图缓冲区尺寸为窗口实际像素 canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.style.overflow = "hidden"; const ctx = canvas.getContext("2d"); // ... 其他代码
2. 监听窗口resize事件,动态更新Canvas尺寸
窗口大小变化时,同步更新绘图缓冲区尺寸,避免再次错位:
// 在main.js末尾添加 window.addEventListener('resize', () => { // 保存当前画布内容(可选,需保留绘制内容时启用) const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // 更新尺寸 canvas.width = window.innerWidth; canvas.height = window.innerHeight; // 恢复画布内容 ctx.putImageData(imageData, 0, 0); // 重新绘制背景色 ctx.fillStyle = canvasColor; ctx.fillRect(0, 0, canvas.width, canvas.height); });
3. 修正初始化时的背景绘制
原来使用screen.width/screen.height是屏幕整体尺寸,改为canvas.width/canvas.height匹配绘图缓冲区:
// 替换原背景绘制代码 ctx.fillStyle = canvasColor; ctx.fillRect(0, 0, canvas.width, canvas.height);
4. (可选)让正方形以鼠标为中心绘制
当前fillRect的x/y是左上角坐标,若想让鼠标位于正方形中心,调整坐标:
// 在Q键按下的处理函数中修改fillRect ctx.fillRect(mousePos.x - shapeSize/4, mousePos.y - shapeSize/4, shapeSize/2, shapeSize/2);
完整修改后的main.js代码
const canvas = document.getElementById("main-canvas"); // 设置绘图缓冲区尺寸为窗口实际像素 canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.style.overflow = "hidden"; const ctx = canvas.getContext("2d"); const settingsButton = document.getElementById("settings-button"); const settingsMenu = document.getElementById("settings-menu"); let mousePos = { x: undefined, y: undefined }; let settingsState = false let shapeSize = 50 let squareColor = "#0000ff"; let canvasColor = "#000000"; // 初始化背景 ctx.fillStyle = canvasColor; ctx.fillRect(0, 0, canvas.width, canvas.height); document.getElementById("settings-button").onclick = function() { settingsState = !settingsState; if (settingsState === true) { settingsButton.style.backgroundColor = "gray"; settingsMenu.style.visibility = "visible"; } else { settingsButton.style.backgroundColor = "white"; settingsMenu.style.visibility = "hidden"; } } document.getElementById("shape-size").onchange = function() { let shapeSizeValue = document.getElementById("shape-size"); let shapeSizeLabel = document.getElementById("shape-size-label"); shapeSizeLabel.innerHTML = "Shape Size: " + shapeSizeValue.value; shapeSize = parseInt(shapeSizeValue.value) // 转为数字,避免除法时字符串拼接 } document.getElementById("square-color").onchange = function() { let squareColorValue = document.getElementById("square-color"); squareColor = squareColorValue.value } document.getElementById("circle-color").onchange = function() { let circleColorValue = document.getElementById("circle-color"); // 补充缺失的赋值逻辑 // circleColor = circleColorValue.value; } document.getElementById("rectangle-color").onchange = function() { let rectangleColorValue = document.getElementById("rectangle-color"); // 补充缺失的赋值逻辑 // rectangleColor = rectangleColorValue.value; } document.getElementById("triangle-color").onchange = function() { let triangleColorValue = document.getElementById("triangle-color"); // 补充缺失的赋值逻辑 // triangleColor = triangleColorValue.value; } document.getElementById("canvas-color").addEventListener("input", (event) => { let canvasColorValue = document.getElementById("canvas-color"); canvasColor = canvasColorValue.value; // 保存当前背景色 ctx.fillStyle = canvasColor; ctx.fillRect(0, 0, canvas.width, canvas.height); }); window.addEventListener('mousemove', (event) => { mousePos = { x: event.clientX, y: event.clientY }; document.getElementById("settings-button").innerHTML = mousePos.x.toString() + " " + mousePos.y.toString() }); window.onkeydown = function(event) { if (event.key === "q") { ctx.fillStyle = squareColor; // 可选:让正方形以鼠标为中心 ctx.fillRect(mousePos.x - shapeSize/4, mousePos.y - shapeSize/4, shapeSize/2, shapeSize/2); // 原代码(左上角对齐鼠标): // ctx.fillRect(mousePos.x, mousePos.y, shapeSize/2, shapeSize/2); } if (event.key === "w") { } if (event.key === "e") { } if (event.key === "r") { } } // 监听窗口resize,更新canvas尺寸 window.addEventListener('resize', () => { // 保存当前画布内容(可选,需保留绘制内容时启用) const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // 更新尺寸 canvas.width = window.innerWidth; canvas.height = window.innerHeight; // 恢复内容 ctx.putImageData(imageData, 0, 0); // 重新绘制背景 ctx.fillStyle = canvasColor; ctx.fillRect(0, 0, canvas.width, canvas.height); });
额外说明
- 需将
shapeSize转为数字类型(parseInt(shapeSizeValue.value)),避免字符串拼接导致计算错误。 - 原代码中
circle-color等颜色选择器的赋值逻辑缺失,可根据功能需求补充。
内容的提问来源于stack exchange,提问作者SpaceFlier
相关产品推荐
相关产品推荐

