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

适配devicePixelRatio后,如何正确捕获Canvas的鼠标/触摸位置?

修正Canvas适配DPR后的坐标捕获问题

当你通过window.devicePixelRatio放大Canvas的实际像素尺寸后,DOM元素的显示尺寸(用户视觉上的大小)和Canvas内部的像素缓冲区尺寸已不匹配,直接用触摸/鼠标位置计算会出现偏移。需要做两步核心修正:

1. 同步Canvas的实际像素尺寸与显示尺寸

设置Canvas的width/height(实际像素缓冲区大小)时,必须同时指定CSS显示尺寸,避免浏览器自动拉伸Canvas导致的坐标错位:

const canvas = canvasRef.current
const context = canvas.getContext('2d')

context.globalCompositeOperation = 'source-over'
context.clearRect(0, 0, canvas.width, canvas.height)

const image = new Image()
image.src = backgroundImage
image.onload = () => {
  onLoadCanvas()

  const devicePixelRatio = window.devicePixelRatio || 1
  const displayWidth = window.innerWidth
  const displayHeight = window.innerHeight

  // 设置Canvas实际像素缓冲区大小(适配DPR)
  canvas.width = displayWidth * devicePixelRatio
  canvas.height = displayHeight * devicePixelRatio
  // 设置Canvas页面显示尺寸(与窗口一致)
  canvas.style.width = `${displayWidth}px`
  canvas.style.height = `${displayHeight}px`

  context.drawImage(image, 0, 0, canvas.width, canvas.height)
}

2. 修正触摸/鼠标坐标映射逻辑

事件获取的位置是基于Canvas显示尺寸的,需要除以devicePixelRatio才能映射到Canvas内部的像素坐标:

const canvas = canvasRef.current
const devicePixelRatio = window.devicePixelRatio || 1
const rect = canvas.getBoundingClientRect()

let x, y
if (isMobile) {
  x = (e.touches[0].pageX - rect.left) / devicePixelRatio
  y = (e.touches[0].pageY - rect.top) / devicePixelRatio
} else {
  // 桌面端用clientX/clientY替代layerX,提升兼容性
  x = (e.clientX - rect.left) / devicePixelRatio
  y = (e.clientY - rect.top) / devicePixelRatio
}

关键说明:

  • canvas.width/canvas.height是Canvas的实际像素缓冲区大小,决定画面清晰度;
  • canvas.style.width/canvas.style.height是Canvas在页面的显示尺寸,必须和窗口大小匹配;
  • 触摸/鼠标事件的坐标是基于显示尺寸的,必须通过devicePixelRatio转换,才能对应到Canvas内部的绘制坐标。

内容的提问来源于stack exchange,提问作者Diego Cândido

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 06:25:05