# Canvas
# Canvas 面试总览:位图画布、状态机与工程边界
⚡ 30 秒速记
Canvas是即时模式的位图画布:浏览器只保留像素,不保留圆、线、文本这些可查询的图形对象- 一次绘制由「设置状态 → 建路径 → 描边或填充」组成;
save()/restore()管理的是绘图状态栈,不是像素快照 - 高清屏模糊的根因是位图像素不足:位图尺寸乘
devicePixelRatio,再缩回CSS尺寸并缩放坐标系 - 动画要在每帧执行「更新状态 → 清屏 → 重绘」,通常用
requestAnimationFrame,离屏后要暂停并释放资源 - 大量动态图元、像素处理适合
Canvas;需要节点语义、无障碍和逐元素事件时优先SVG/DOM
Canvas 本质上是即时绘制的位图画布,浏览器保留的是像素,而不是可查询的图形节点。 绘制通常是设置状态、建立路径再执行 fill() 或 stroke();save() 和 restore() 管理绘图状态,并不会保存像素。高清屏要同时处理位图尺寸、CSS 显示尺寸和 devicePixelRatio,动画则用 requestAnimationFrame 持续清屏重绘。大量动态图元适合 Canvas,需要节点语义、无障碍或逐元素事件时更适合 SVG 或 DOM。
CanvasRenderingContext2D 可以看成一台带状态的画笔。fillStyle、strokeStyle、变换矩阵、裁剪区等属性会影响之后的命令;beginPath() 只会清空当前路径,不会擦除画布像素,真正落笔要调用 fill() 或 stroke()。这也是常见 bug 的来源:忘记 beginPath() 会把旧子路径再次描边,忘记 restore() 会让缩放、透明度继续污染后续绘制。
工程里通常把「数据状态」作为事实来源,而不是试图从像素里还原对象。以拖拽图形为例,需要在 JavaScript 中保存每个图形的位置和命中区域,指针移动时自己做 hit test,然后清屏并按新状态完整重绘。复杂场景可以把静态背景画到离屏画布,只重绘变化层;像素计算可交给 Web Worker 或 OffscreenCanvas,但主线程上的 DOM 事件和最终合成仍要纳入性能预算。
高清适配必须区分两种尺寸:canvas.width/height 决定位图缓冲区,style.width/height 决定页面显示大小。只写 CSS 尺寸会把较少的物理像素拉伸;只放大位图又不缩放坐标系,会让逻辑尺寸改变。上面的实验会真实读取当前设备的 devicePixelRatio、重建 backing store,并从绘制结果采样像素,能直观看出两条路径的差别。
💬 面试官追问
-
代码里连续画两条独立折线,第二次调用
stroke()时第一条线又被描了一遍;同事认为stroke()后路径会自动清空,你怎么判断?stroke()只把当前路径描到位图上,不会清空路径,因此旧子路径仍会参与下一次描边。每组独立路径应在构造前调用beginPath();它只重置当前路径,不会清除已经落到画布上的像素。 -
一个流程图编辑器支持拖拽节点,产品要求刷新重绘后节点仍可命中,你会把节点位置和选中状态存在哪里?
节点位置、层级和命中区域应保存在
JavaScript数据状态中,像素只作为状态的绘制结果。指针事件先做hittest,再更新数据并清屏重绘;若试图从画布像素反推业务对象,会丢失语义且难以维护交互状态。 -
监控大屏的静态底图很复杂,只有少量指标每帧变化;如果整张画布重绘已经挤占主线程,你会先改哪一层?
我会把静态背景预绘到离屏画布,更新时复用它,只重绘动态层并完成合成。像素计算较重时可评估
Web Worker或OffscreenCanvas;但DOM事件处理和最终合成成本仍需测量,不能假设移出部分计算就消除了主线程压力。 -
组件库统一执行了
ctx.scale(2, 2),随后同一上下文中的透明度和裁剪区域也异常影响后续组件,你会怎样定位状态泄漏?先检查每段局部绘制是否用
save()和restore()成对包住变换、透明度及裁剪设置,并确认异常路径没有提前返回而漏掉恢复。CanvasRenderingContext2D是有状态画笔,属性会持续影响后续命令;仅调用beginPath()无法恢复这些状态。 -
设计师要求签名板在
2x屏清晰,但导出文件必须保持固定逻辑尺寸,你会怎样处理显示和导出的冲突?显示时把
canvas.width/height设为CSS尺寸乘devicePixelRatio,保留原CSS尺寸,并用setTransform()将逻辑坐标统一回来。固定逻辑尺寸的导出应另行绘制或缩放到目标缓冲区;直接导出高清backingstore会得到更大的物理像素图。 -
地图上两万个点要支持
hover,交互负责人坚持使用Canvas,无障碍负责人希望每个点都能键盘聚焦,你会怎么拆解选型?Canvas可通过网格、四叉树或颜色拾取降低命中成本,但不会天然提供每个点的DOM语义和键盘焦点。若逐点可访问性是硬约束,应评估SVG、DOM或分层混合方案;选择语义节点会增加节点管理成本,却能避免自行重建整套交互语义。