← 全部文章

技术笔记 · 2026-09-14

Canvas 点选为什么会偏移:统一绘制与命中坐标

把视口坐标、画布逻辑坐标、业务几何与像素密度分开,分析缩放和页面滚动后图形可见但点选不准的原因。

延伸分析 · 从项目场景展开的原理与方案讨论。

  • Canvas
  • 坐标变换
  • 命中检测
  • 跨端交互

关联项目:定制家具设计与报价系统。项目中的绘制与点选使用几何换算;本文以此为起点分析通用坐标模型,也适用于流程图、座位图和布局编辑器。

同一个点,可以有四种坐标

事件提供的位置通常属于视口,Canvas 在页面里有自己的显示区域,绘制逻辑使用画布内部的坐标,业务对象又可能以毫米或网格单位表示。高像素密度还会让实际位图尺寸大于 CSS 显示尺寸。

这些数值看起来都叫 x、y,含义却不同。把它们直接混用,常见结果是页面不滚动时恰好正确,缩放或换设备后就逐渐偏移。

先把事件坐标映射到逻辑画布

以下公式只覆盖无边框、无内边距、没有旋转和斜切的轴向缩放场景。rect 与事件位置必须使用同一套视口坐标。

localX = (clientX - rect.left) × logicalWidth / rect.width
localY = (clientY - rect.top)  × logicalHeight / rect.height

Web 中的 getBoundingClientRect() 返回相对视口的位置,但包含边框与内边距;存在这些样式时,需要求实际内容区域,不能直接套用上式。MDN 的说明有助于确认坐标基准。

项目在点击时重新查询画布区域,这能避免长期缓存页面滚动前的位置。跨端适配时还要核对宿主事件字段的坐标含义,不能仅凭字段名相似就复用。

业务几何使用同一份变换

绘制常常先缩放,再把图形平移到画布中央。点选时要做反向换算:先减去平移,再除以缩放。两边应共享缩放比例和预览区域,而不是分别猜一个“差不多”的偏移。

绘制:screen = model × scale + offset
命中:model  = (screen - offset) / scale

如果未来支持旋转、多层缩放,应把完整变换组合成矩阵并使用逆矩阵回到模型空间。不能在轴向公式上不断叠加修正常数。

DPR 处理清晰度,不重定义业务尺寸

Canvas 位图可以按设备像素比扩充,再将绘制上下文缩放到逻辑坐标。此时输入和命中仍可使用同一份逻辑尺寸,不要再把点击位置重复乘一次 DPR。devicePixelRatio 文档给出了显示尺寸与位图分辨率的区别。

重新设置 Canvas 的宽高后,需要重新建立上下文状态与缩放。否则一次重绘可能使用旧比例,下一次又回到默认比例。

命中优先级也是规则的一部分

两个矩形共边或者覆盖时,一个点可能命中多个对象。应明确谁优先:视觉最上层、当前可编辑对象,或已有业务约定。文字、标尺与空白区域是否可点击也要显式决定。

验证时不要只点图形中心。应覆盖边界、共边、留白、滚动后、窗口改变后以及不同 DPR。对于每个模型测试点,先做正向绘制换算,再做逆向还原,结果应在允许误差内回到原点。