Canvas.width/height与CSS尺寸不匹配是失真主因:浏览器强行拉伸致模糊、坐标偏移、isPointInPath失效;DPR>1时需满足canvas.width≈CSS width×DPR,校准鼠标坐标须用(clientX−rect.left)×(canvas.width/rect.width)。

Canvas.width/height 和 CSS width/height 是否对齐
尺寸失真最常见原因是 canvas.width / canvas.height(像素分辨率)和 style.width / style.height(显示尺寸)数值不匹配。浏览器会强行拉伸画布内容来填满 CSS 尺寸,导致绘图模糊、坐标偏移、圆形点击检测失效。
在控制台直接运行这行代码验证:
console.log( 'canvas.width:', canvas.width, 'canvas.height:', canvas.height, 'CSS width:', parseFloat(getComputedStyle(canvas).width), 'CSS height:', parseFloat(getComputedStyle(canvas).height) )
- 若
canvas.width≠parseFloat(getComputedStyle(canvas).width)(且设备 DPR = 1),说明 HTML 属性或 JS 赋值没生效 - 若 DPR > 1(如 MacBook 或 Windows 缩放 125%),则应满足:
canvas.width ≈ CSS width × DPR,否则就是高清屏适配漏了 - 不要依赖
offsetWidth/offsetHeight—— 它们受父容器 transform 影响,不准
getBoundingClientRect() 返回值是否被误用
鼠标坐标计算必须基于 getBoundingClientRect(),但很多人拿了 rect.left/top 就直接减,忘了它返回的是「当前显示尺寸下的视口坐标」,而 canvas 内部坐标系可能已被缩放或拉伸。
典型错误写法:
立即学习“前端免费学习笔记(深入)”;
const x = e.clientX - rect.left; // ❌ 忽略了 DPR 和 canvas.width/rect.width 比例
正确校准步骤(缺一不可):
- 先取
const rect = canvas.getBoundingClientRect() - 再算绘图坐标:
const x = (e.clientX - rect.left) * (canvas.width / rect.width) - 同理处理 y:
const y = (e.clientY - rect.top) * (canvas.height / rect.height) - 避免用
event.offsetX/event.offsetY—— 在缩放、transform 或嵌套容器中完全不可靠
ctx.scale() 是否被重复调用或意外重置
高清屏下常用 ctx.scale(dpr, dpr) 让绘图逻辑保持在“CSS 像素”坐标系,但这个状态是上下文的一部分,极易被破坏。
两个高频陷阱:
- 每次 resize 后执行
canvas.width = canvas.width—— 这会清空画布并重置所有变换矩阵,ctx.scale()失效,后续绘图全按 1:1 坐标跑偏 - 未在缩放前调用
ctx.setTransform(1, 0, 0, 1, 0, 0),导致多次scale()叠加(比如 2×2×2=8 倍),坐标爆炸式偏移 - 检查是否在
requestAnimationFrame循环里反复scale(),而没做防重入
Canvas 是否被 CSS 隐式拉伸或塌缩
Canvas 是替换元素,默认 inline 行内行为,vertical-align: baseline 会在底部留白;Flex 容器中若没设宽高,它可能塌成 0×0;百分比 CSS 宽高 + 缺失 HTML width/height 属性,也会触发浏览器默认 300×150 像素 + 拉伸渲染。
快速排查项:
- 检查 computed style:是否有
display: inline?→ 加display: block或vertical-align: top - 父容器是
display: flex?→ 确保 canvas 有显式width和height属性(哪怕只是占位用的1) - 是否用了
width: 100%但没同步设置canvas.width?→ 这是圆形点击失效的头号原因 - 是否在 canvas 上写了
transform: scale()?→ 会干扰getBoundingClientRect(),改用ctx.scale()更可控
真正难缠的不是某一个参数错,而是多层缩放叠加:CSS 拉伸 + DPR 缩放 + ctx.scale() + 重复 setTransform 缺失 —— 最终坐标偏差可能放大数倍。调试时别猜,每一步都打日志看数值,尤其关注 canvas.width / rect.width 这个比值是否稳定等于预期 DPR。



















