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
相关产品推荐
相关产品推荐

