如何用原生JavaScript解决滚动时Canvas光标位置偏移问题?
问题描述
我做了个简单Canvas,鼠标在上面移动时,会生成两条穿过鼠标位置、分别从对应侧边延伸到对侧的线条。正常状态下功能没问题,但滚动页面后坐标就出错了——水平线不再对准鼠标,而且显示的x、y坐标数值也保持不变。
举个例子:把鼠标移到Canvas的(175, 360)位置后向下滚动页面,再移动鼠标时,水平线不跟着鼠标走了,但坐标还是显示(175, 360)。
需求:怎么修正坐标,不让页面滚动影响数值?
注:我试过clientRect()和offset相关方法,但不太熟,别没看代码就随便给这类方案,除非确定可行。
原代码
JavaScript
const canvas = document.querySelector('#canvas'); const context = canvas.getContext("2d"); const xCoordinate = document.querySelector('#x-coordinate'); const yCoordinate = document.querySelector('#y-coordinate'); canvas.width = 1024; canvas.height = 550; function drawLines(x, y) { //From top down context.beginPath(); context.moveTo(x, 0); context.lineTo(x, canvas.height); context.stroke(); //From left to right context.beginPath(); context.moveTo(0, y); context.lineTo(canvas.width, y); context.stroke(); } function removeLines(x, y) { //Clears top left as you move context.clearRect(0, 0, x, y); //Clears top right as you move context.clearRect(x + 1, 0, canvas.width, y); //Clears bottom left as you move context.clearRect(0, y + 1, x, canvas.height); //Clears bottom right as you move context.clearRect(x + 1, y + 1, canvas.width, canvas.height); } context.canvas.addEventListener('mousemove', function(event) { let mouseX = event.clientX - context.canvas.offsetLeft; let mouseY = event.clientY - context.canvas.offsetTop; //let mouseX = event.x - canvas.offsetLeft; //let mouseY = event.y - canvas.offsetTop; xCoordinate.innerHTML = mouseX; yCoordinate.innerHTML = mouseY; drawLines(mouseX, mouseY); removeLines(mouseX, mouseY); })
CSS
#canvas { border: solid black 1px; } p { font-size: 14pt; font-weight: 600; }
HTML
<!DOCTYPE html> <html lang="en"> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE-edge"> <meta name="viewport", content="width=device-width, initial-scale=1.0"> <meta name="author" content="Christian Davis"> <link rel="stylesheet" href="styles.css"> <title>Mouse Coordinates On A Canvas</title> </head> <body> <p>(<span id="x-coordinate">0</span>, <span id="y-coordinate">0</span>)</p> <canvas id="canvas" style="width: 1024px; height: 550px;"></canvas> <script src="app.js"></script> </body> </html>
解决方案
问题核心:你用clientX/clientY搭配offsetLeft/offsetTop计算坐标,offsetLeft/offsetTop是元素相对于父元素的静态偏移量,不会随页面滚动变化,但clientX/clientY是相对于当前视口的位置,滚动后Canvas在视口中的实际偏移已经改变,所以计算出来的坐标就错了。
正确的做法是用getBoundingClientRect()获取Canvas在视口中的实时位置,再计算鼠标相对Canvas的坐标:
修正后的JavaScript代码
const canvas = document.querySelector('#canvas'); const context = canvas.getContext("2d"); const xCoordinate = document.querySelector('#x-coordinate'); const yCoordinate = document.querySelector('#y-coordinate'); canvas.width = 1024; canvas.height = 550; function drawLines(x, y) { // 垂直竖线 context.beginPath(); context.moveTo(x, 0); context.lineTo(x, canvas.height); context.stroke(); // 水平横线 context.beginPath(); context.moveTo(0, y); context.lineTo(canvas.width, y); context.stroke(); } function removeLines(x, y) { // 清除各区域旧线条 context.clearRect(0, 0, x, y); context.clearRect(x + 1, 0, canvas.width, y); context.clearRect(0, y + 1, x, canvas.height); context.clearRect(x + 1, y + 1, canvas.width, canvas.height); } canvas.addEventListener('mousemove', function(event) { // 获取Canvas当前在视口中的实时位置 const rect = canvas.getBoundingClientRect(); // 计算鼠标在Canvas内部的相对坐标 const mouseX = event.clientX - rect.left; const mouseY = event.clientY - rect.top; // 取整让显示更美观 xCoordinate.textContent = Math.round(mouseX); yCoordinate.textContent = Math.round(mouseY); drawLines(mouseX, mouseY); removeLines(mouseX, mouseY); })
关键说明
getBoundingClientRect()返回的rect.left和rect.top是Canvas左上角在当前视口中的坐标,会自动包含页面滚动的偏移量,所以用clientX/clientY减去这两个值,就能得到准确的鼠标在Canvas内部的相对位置。- 把
innerHTML换成textContent更安全,避免意外注入风险;坐标取整让显示更整洁。 - 你的HTML中Canvas的属性
width/height和行内样式尺寸一致,没问题,若后续调整尺寸,记得保持两者比例,防止Canvas内容拉伸变形。
内容的提问来源于stack exchange,提问作者That New Coder
相关产品推荐
相关产品推荐

