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

如何用原生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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:05:12