canvas宽高必须用html属性设置,css仅缩放显示尺寸;交互坐标需手动转换,考虑dpr和getboundingclientrect;混排时注意z-index与pointer-events;离屏canvas仅作静态缓存,交互控件应保留在dom中。

Canvas 元素不是“占位符”,它本身就是一个像素缓冲区容器;交互失效或坐标错位,几乎全是初始化或事件映射环节出的问题。
canvas 宽高必须写在 HTML 属性里,不能只靠 CSS
这是绝大多数交互错位的根源。CSS 设置的 width/height 只缩放显示尺寸,不改变底层像素缓冲区大小,导致 canvas 坐标系与 DOM 坐标系严重脱节。
- ✅ 正确:
<canvas id="chart" width="1200" height="600"></canvas> - ❌ 错误:
<canvas id="chart" style="width:1200px;height:600px"></canvas>(会拉伸、模糊、getBoundingClientRect()返回值与ctx坐标不匹配) - 若需响应式,应在 JS 中监听
resize,按比例重设canvas.width和canvas.height,再调用ctx.scale()补偿缩放
鼠标事件坐标必须手动转换到 canvas 坐标系
Canvas 没有内置事件绑定能力,所有点击/拖拽都得靠 event.clientX/event.clientY 减去 canvas 左上角在视口中的偏移,再除以设备像素比(DPR)才能对齐。
- 先获取 canvas 在视口中的位置:
const rect = canvas.getBoundingClientRect() - 再换算点击点:
const x = (e.clientX - rect.left) * canvas.width / rect.width - 同理计算
y;注意:rect.width是 CSS 显示宽,canvas.width是实际像素宽,二者不等时必须做这个归一化 - 移动端还要处理
touchstart的touches[0],逻辑一致
DOM + Canvas 混排时,z-index 和 pointer-events 要协同控制
Canvas 是位图层,DOM 是盒模型层,混排时视觉层级和事件捕获容易打架。
- Canvas 上方叠 DOM 元素(如按钮、标签),需确保其
z-index高于 canvas,且 canvas 父容器无overflow: hidden截断 - 若 DOM 元素要穿透到 canvas 底层响应事件,给它加
style="pointer-events: none",但注意这会让它完全不响应任何鼠标事件 - Canvas 自身默认
pointer-events: auto,不需要额外设置;但若 canvas 作为背景图层且不需交互,可设为none避免遮挡下方 DOM - 避免在 canvas 上直接放
input或textarea—— 它们无法与 canvas 像素坐标对齐,输入框光标位置会漂移
离屏 canvas 缓存静态内容,但别让它干扰主交互流
用离屏 canvas 预绘文字、图标、底图能显著提速,但它一旦被 drawImage() 到主 canvas,就彻底失去语义,不能再响应事件。
- 缓存对象必须保留原始几何信息(如文字起始坐标、图标包围盒),用于后续坐标映射和碰撞检测
- 不要把整个 UI 层都塞进离屏 canvas;按钮、开关等需交互控件,应保持为真实 DOM 元素
- 若用
drawImage(offlineCanvas, ...)后又想支持点击某图标,得自己维护一份iconList = [{x, y, w, h, id}],在 mousemove/mousedown 里做矩形命中判断 - 离屏 canvas 尺寸务必紧凑,比如只画一个 16×16 图标就设
width=16、height=16,过大浪费内存且drawImage更慢
最易被忽略的是 DPR 归一化 —— 高分屏下,getBoundingClientRect().width 和 canvas.width 的比值不是 1,漏掉这步,坐标永远差一倍。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











