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

Canvas绘图在桌面端正常但触屏端点击位置偏移问题求助

Canvas绘图在桌面端正常但触屏端点击位置偏移问题求助

我正在做一个基于Canvas的绘图项目,目前桌面端的基础操作(鼠标拖拽绘图、清除画布)都能正常工作,但切换到触屏设备时,绘制的位置总是和实际点击的地方偏移,会往右下方向错位一段距离。

下面是我代码里处理移动事件的核心逻辑:

function onMove(ev) {
    if (!gIsDrawing) return

    gCtx.fillStyle = gBackgroundColor
    gCtx.strokeStyle = gShape.color
    gCtx.lineWidth = gShape.size

    const pos = getEvPos(ev)
    //* Calc the delta, the diff we moved
    const dx = pos.x - gPrevPos.x
    const dy = pos.y - gPrevPos.y
    move(dx, dy)
    gCtx.fillStyle = gBackgroundColor

    if (gShape.shape === 'pen') {
        gCtx.lineTo(pos.x, pos.y)
        gCtx.stroke()
    } else if (gShape.shape === 'square') {
        gCtx.strokeRect(pos.x, pos.y, 50, 50)
        gCtx.fillRect(pos.x, pos.y, 50, 50)
    } // ...其他图形的处理逻辑
}

问题分析与解决思路

这种触屏偏移问题通常有两个核心原因:

  • 触屏事件与鼠标事件的坐标获取方式不同:鼠标事件直接用ev.clientX/clientY,但触屏事件的坐标存储在ev.touches[0]对象里,不能直接复用鼠标事件的坐标获取逻辑。
  • Canvas的实际尺寸与显示尺寸不匹配:如果你的Canvas用CSS设置了宽高(比如width: 100%),但没有同步设置Canvas元素的width/height属性,会导致坐标缩放错位,触屏时这个偏移会更明显。

具体修复方案

1. 修正坐标获取函数getEvPos,兼容鼠标和触屏事件

把你的getEvPos函数改成下面这样,同时处理坐标的缩放转换:

function getEvPos(ev) {
    // 先获取原始坐标
    let clientX, clientY;
    if (ev.type.includes('touch')) {
        // 触屏事件取第一个触摸点的坐标
        const touch = ev.touches[0];
        clientX = touch.clientX;
        clientY = touch.clientY;
    } else {
        // 鼠标事件直接取坐标
        clientX = ev.clientX;
        clientY = ev.clientY;
    }

    // 获取Canvas在页面中的位置和显示尺寸
    const canvasRect = gCanvas.getBoundingClientRect();
    // 计算Canvas实际尺寸与显示尺寸的缩放比例
    const scaleX = gCanvas.width / canvasRect.width;
    const scaleY = gCanvas.height / canvasRect.height;

    // 转换为Canvas内部的相对坐标
    return {
        x: (clientX - canvasRect.left) * scaleX,
        y: (clientY - canvasRect.top) * scaleY
    };
}

2. 完善触屏事件的绑定与处理

确保你绑定了正确的触屏事件,并且阻止默认行为避免页面滚动干扰:

// 触屏开始事件
gCanvas.addEventListener('touchstart', (ev) => {
    ev.preventDefault(); // 阻止页面滚动等默认触摸行为
    gIsDrawing = true;
    const pos = getEvPos(ev);
    gPrevPos = pos;
    gCtx.beginPath(); // 开始新的绘图路径
    gCtx.moveTo(pos.x, pos.y);
});

// 触屏移动事件
gCanvas.addEventListener('touchmove', (ev) => {
    ev.preventDefault();
    onMove(ev);
});

// 触屏结束事件
gCanvas.addEventListener('touchend', () => {
    gIsDrawing = false;
});

这样修改后,触屏时的坐标就能准确对应Canvas的绘图区域,不会再出现偏移问题了。

备注:内容来源于stack exchange,提问作者Dolev Levy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 11:19:39