Canvas绘图需按序执行:先获取已挂载DOM的2D上下文,再调用beginPath()、stroke()/fill();drawImage()须在img.onload内调用并处理跨域;clearRect/fillRect坐标原点在左上角;save()/restore()需成对使用且不跨函数;Canvas是无图层、无撤销的状态机模型。

Canvas 绘图不是“调用一个函数就出图”,而是需要先获取上下文、明确坐标系、手动触发绘制路径并提交渲染——漏掉 beginPath() 或忘了 stroke()/fill(),画布上什么都不会出现。
如何正确获取 2D 绘图上下文
必须通过 getContext('2d') 获取,且只能在 <canvas></canvas> 元素已挂载 DOM 后调用。常见错误是脚本执行早于元素解析,导致返回 null。
- 确保 JS 在
</body>前执行,或监听DOMContentLoaded - 检查元素是否存在:
const canvas = document.getElementById('myCanvas'); if (!canvas) throw new Error('Canvas element not found'); -
getContext('2d')返回的是CanvasRenderingContext2D实例,不是普通对象,不支持链式调用(如ctx.beginPath().moveTo(...)会报错)
为什么 drawImage() 不显示图片
最常被忽略的是图片加载时机:drawImage() 不会等待图片加载完成,若在 img.onload 外调用,图像数据为空。
- 必须把
drawImage()放在img.onload回调内 - 不要用
new Image().src = '...'后立刻绘图;应显式绑定onload -
跨域图片需设置
crossOrigin属性,否则会触发安全异常:const img = new Image(); img.crossOrigin = 'anonymous'; img.src = 'https://example.com/image.png'; img.onload = () => ctx.drawImage(img, 0, 0);
clearRect() 和 fillRect() 的坐标陷阱
Canvas 坐标原点在左上角,x 向右递增,y 向下递增;但 clearRect() 和 fillRect() 的参数顺序都是 (x, y, width, height),和 CSS 的 top left width height 不同。
立即学习“Java免费学习笔记(深入)”;
-
clearRect(10, 20, 100, 50)清除的是「从第 10 列、第 20 行开始,宽 100、高 50」的矩形区域 - 若 canvas 宽高为
width=300、height=200,则有效y范围是0 ~ 199;传入y=200不会报错,但无效果 - 缩放后(
ctx.scale(2, 2)),坐标值仍按原始单位传入,但绘制结果会放大——fillRect(10, 10, 20, 20)实际占 40×40 像素
save() / restore() 不生效?检查调用位置
save() 只保存当前变换状态(平移、旋转、缩放、裁剪路径、全局 alpha 等),不保存已绘制内容;restore() 必须与 save() 成对出现,且不能跨函数边界隐式配对。
- 每次
save()都压栈,restore()弹栈;嵌套过深或漏调restore()会导致栈溢出或状态错乱 - 不要在循环中无条件
save()而不restore(),尤其在动画帧(requestAnimationFrame)里 - 示例:旋转单个图形而不影响后续绘制
ctx.save(); ctx.translate(100, 100); ctx.rotate(Math.PI / 4); ctx.fillRect(-20, -20, 40, 40); ctx.restore(); // 必须这里恢复,否则后续 draw 都带旋转
Canvas 是状态机模型,所有操作都依赖当前上下文状态;没有“撤销”“图层”“自动重绘”这些概念——画完就没了,改图只能清屏重画,或者自己维护绘图数据并重新执行绘制逻辑。










