Canvas鼠标交互的核心是用数学判断点是否落在图形内,需将clientX/clientY转为Canvas坐标系,处理CSS缩放与高DPR适配,并手动实现矩形、圆形等几何判定。

Canvas鼠标交互的核心不是“给图形加事件”,而是“用数学判断点是否落在图形里”。它不依赖DOM事件冒泡,全靠坐标换算 + 几何判定 + 手动触发逻辑。
准确获取鼠标在Canvas内的位置
浏览器给的clientX/clientY是页面坐标,必须转成Canvas坐标系才能用。最稳的方式是:
- 每次事件中调用
canvas.getBoundingClientRect(),拿到Canvas左上角在视口中的真实位置 - 用
e.clientX - rect.left和e.clientY - rect.top得到画布内坐标(x, y) - 如果Canvas用了CSS缩放(如
transform: scale(1.5)),要除以对应缩放值;更推荐用ctx.scale()做内部缩放,避免坐标错乱 - 高DPR屏幕(如MacBook Retina)下,确保
canvas.width/height设为物理像素尺寸,并用ctx.scale(dpr, dpr)绘图,鼠标坐标仍按CSS尺寸计算,无需额外除dpr
判断鼠标是否命中某个图形
Canvas没有内置“点击某条线”或“点击某个区域”的能力,所有判定都得自己写。不同图形用不同方法:
- 矩形:检查
x是否在[left, right]、y是否在[top, bottom] - 圆形:用距离公式
Math.hypot(x - cx, y - cy) - 多边形(如省份轮廓):用射线法——从点向右水平发一条射线,统计与多少条边相交;奇数次相交即在内部
- 复杂区域(含岛屿、洞):需解析GeoJSON的
coordinates结构,区分外环和内环,不能简单合并顶点
组织可维护的交互逻辑
别把所有判定塞进一个click监听器里。建议分层管理:
- 把每个可交互区域的数据单独存为对象,包含
id、name、points(顶点数组)、onSelect回调等字段 - 点击时遍历区域数组,对每个调用判定函数,第一个返回
true的即为命中目标 - 命中后不直接重绘,而是触发业务回调(如
onRegionClick('广东', x, y)),由上层决定高亮、加载下级数据或弹窗 - 加简单防抖或最小点击面积(如3×3像素),避免误触
性能与兼容性要点
上千个区域逐个射线法检测会卡顿,实际项目中要注意:
- 先做粗筛:用包围盒(bounding box)快速排除明显不相关的区域
- 动态场景下,只检测当前视口内或可见层级的图形
- 移动端要用
touchstart替代click,并处理preventDefault()防止滚动干扰 - 避免用
offsetLeft/offsetTop算位置——受父元素定位、边框、padding影响,误差大


















