Canvas绘图需手动触发渲染,所有操作在内存中准备指令,调用fill()、stroke()等才上屏;必须先获取DOM元素再调用getContext('2d'),参数错误或元素未加载会导致返回null而报错;需确保HTML中存在canvas元素且JS执行时DOM已就绪;'2d'大小写敏感;高DPI需同步设置canvas.width/height并scale;样式无继承,需显式设置fillStyle、strokeStyle等;lineWidth=1易模糊,建议设为2并translate(0.5,0.5);save()/restore()可隔离状态;beginPath()必须在每次新路径前调用,否则旧路径累积;路径不调用fill()/stroke()则不可见,且提交后即消耗,不可复用。

Canvas 绘图不是“画完就显示”,而是必须手动触发渲染——getContext('2d') 返回的绘图上下文不自动绘制,所有操作都只是在内存中准备指令,直到你调用 fill()、stroke() 或 drawImage() 等才会真正上屏。
获取 Canvas 元素和 2D 上下文
必须先拿到 DOM 元素,再调用 getContext('2d');传错参数(比如 'webgl' 却没写 WebGL 逻辑)或元素未加载完成,都会返回 null,后续调用会直接报 TypeError: Cannot read property 'xxx' of null。
- 确保
<canvas id="myCanvas"></canvas>已存在于 HTML 中,且 JS 执行时 DOM 已就绪(放在</body>前,或监听DOMContentLoaded) -
getContext('2d')是唯一合法的 2D 上下文类型,'2d'必须是字符串,大小写敏感 - 若需高 DPI 支持(如 Retina 屏),不能只靠 CSS 设置宽高,还要同步修改
canvas.width和canvas.height属性
设置绘图样式与坐标系
Canvas 没有“默认样式继承”,每次 fillStyle、lineWidth、font 等属性都是独立状态,且不会自动重置。容易出现“前一个图形设了红色,后面忘了改,结果全红了”这类问题。
-
ctx.fillStyle控制填充色(适用于fillRect()、fill()),ctx.strokeStyle控制描边色(适用于strokeRect()、stroke()) -
ctx.lineWidth默认是 1,但它是“线中心对齐”的,实际像素占用是lineWidth + 1(向上取偶),画 1px 线常偏模糊,建议设为 2 并配合translate(0.5, 0.5) - 调用
ctx.save()/ctx.restore()可保存/恢复当前样式与变换状态,适合局部绘图隔离
绘制路径并提交到画布
路径(path)是 Canvas 的核心抽象,但很多人误以为 beginPath() 是可选的——其实它必须在每次新路径前调用,否则旧路径会持续累积,导致意外重绘或性能下降。
立即学习“Java免费学习笔记(深入)”;
- 标准路径流程:
ctx.beginPath()→ctx.moveTo()/ctx.lineTo()/ctx.arc()… →ctx.fill()或ctx.stroke() -
ctx.closePath()不是必须的,它只是自动加一条线回到起点,但不会自动fill或stroke - 如果只画单个矩形,优先用
ctx.fillRect(x, y, w, h)这类快捷方法,比路径快且无需管理状态
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
<p>// 设置高 DPI 适配(可选但推荐)
const dpr = window.devicePixelRatio || 1;
canvas.width = canvas.clientWidth <em> dpr;
canvas.height = canvas.clientHeight </em> dpr;
ctx.scale(dpr, dpr);</p><p>ctx.fillStyle = '#3498db';
ctx.fillRect(10, 10, 100, 60); // 直接填充矩形</p><p>ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(30, 120);
ctx.lineTo(130, 120);
ctx.stroke();路径对象本身不可见,beginPath() 后不调用 fill() 或 stroke() 就等于什么都没画;而一旦调用,路径即被消耗,再次使用需重新定义——这点和 SVG 的声明式模型完全不同,得时刻想着“我有没有提交”。










