一、含义
beginPath() 是 Canvas 2D 上下文中的一个方法,它的作用是开始一条新的路径,或者重置当前路径。调用这个方法后,之前定义的所有路径都会被清除,后续的路径绘制命令(如 lineTo()、arc()、rect() 等)会开始一个新的路径集合。
二、词源解析
| 单词 | 词源 | 含义 |
|---|---|---|
| begin | 古英语 beginnan(开始) | 开始、启动 |
| Path | 古希腊语 pathos(路径)→ 古英语 pæþ | 路径、轨迹 |
| beginPath | 组合词 | 开始新路径 |
词源故事
path本意是“走出来的路”在计算机图形学中,路径是由一系列点、线、曲线连接而成的轨迹
beginPath()就是“从零开始规划一条新路线”
三、语法
ctx.beginPath();
参数:无
返回值:无
作用:清空当前路径列表,开始一个新的路径
四、为什么需要 beginPath()?
类比:一张纸上的不同线条
想象你在纸上画画:
如果你不换笔,一直画下去,所有线条都是连在一起的
有时你希望画独立的图形(比如分开的两个圆)
你需要抬起笔,移动到新位置,再开始画
beginPath() 就是那个“抬起笔”的动作。
代码对比
没有 beginPath() 的问题
// 画第一个圆 ctx.arc(100, 100, 50, 0, 2 * Math.PI); ctx.strokeStyle = 'red'; ctx.stroke(); // 想画第二个圆(红色) ctx.arc(200, 100, 50, 0, 2 * Math.PI); ctx.stroke(); // 问题:第二个圆会继承之前的所有路径设置,并且和第一个圆共享路径
有 beginPath() 的正确写法
// 画第一个圆 ctx.beginPath(); // 清空路径,开始新路径 ctx.arc(100, 100, 50, 0, 2 * Math.PI); ctx.strokeStyle = 'red'; ctx.stroke(); // 只描边第一个圆 // 画第二个圆 ctx.beginPath(); // 再次清空路径,开始全新的路径 ctx.arc(200, 100, 50, 0, 2 * Math.PI); ctx.strokeStyle = 'red'; // 需要重新设置样式 ctx.stroke(); // 只描边第二个圆
五、路径是什么?
在 Canvas 中,路径是一系列点、线、曲线的集合。
路径的构成
ctx.beginPath(); // 开始新路径 ctx.moveTo(50, 50); // 移动到起点 ctx.lineTo(150, 50); // 画一条线到 (150,50) ctx.lineTo(100, 150); // 画一条线到 (100,150) ctx.closePath(); // 关闭路径(画线回到起点)
路径的状态
路径是累积的:每次调用绘图方法都会添加到当前路径
路径是独立的:不同路径之间不会相互影响
路径可以重复使用:同一个路径可以多次描边或填充
六、路径相关的三个关键方法
| 方法 | 作用 | 类比 |
|---|---|---|
beginPath() | 开始新路径,清空旧路径 | 抬起笔,移动到新位置 |
closePath() | 关闭当前路径(画线回到起点) | 把路径连成封闭图形 |
stroke()/fill() | 真正绘制路径 | 落笔,画出线条 |
示例:绘制两个独立三角形
// 第一个三角形 ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(100, 50); ctx.lineTo(75, 100); ctx.closePath(); // 自动画线回到 (50,50) ctx.fillStyle = 'red'; ctx.fill(); // 第二个三角形(不受第一个影响) ctx.beginPath(); // 关键!不清空的话会和第一个路径混合 ctx.moveTo(150, 50); ctx.lineTo(200, 50); ctx.lineTo(175, 100); ctx.closePath(); ctx.fillStyle = 'blue'; ctx.fill();
七、在五子棋棋盘代码中的作用
for (var j = 0; j < 10; j++) {
for (var i = 0; i < 10; i++) {
cav.beginPath(); // ← 每次绘制小方格前都开始新路径
cav.strokeStyle = "rgb(147,109,70)";
cav.rect(115 + j * 30, 85 + i * 30, 30, 30);
cav.stroke();
}
}为什么每次循环都要 beginPath()?
如果不加 beginPath(),会发生什么?
// 错误示范:没有 beginPath()
for (var j = 0; j < 10; j++) {
for (var i = 0; i < 10; i++) {
// cav.beginPath(); // 省略
cav.rect(115 + j * 30, 85 + i * 30, 30, 30);
cav.stroke();
}
}问题:
第一次循环:定义了第1个矩形路径,描边
第二次循环:又添加第2个矩形到同一个路径中
第100次循环:路径里堆积了100个矩形
每次
stroke()都会把路径中所有矩形重新描边一次
结果:
越到后面的格子,被重复描边的次数越多
线条颜色会叠加,越画越深
性能也会变差
正确做法的好处
每次 beginPath() 清空旧路径,只保留当前要画的这一个矩形,保证每个格子只被描边一次,线条颜色均匀一致。
八、不调用 beginPath() 的后果示意图
第一次循环后路径: [矩形1] 第二次循环后路径: [矩形1, 矩形2] ← 堆积! 第三次循环后路径: [矩形1, 矩形2, 矩形3] ... 第100次循环后路径: [矩形1, 矩形2, ..., 矩形100] 每次 stroke() 都会把路径中所有矩形重新画一遍!
视觉效果:
第一个格子:描边1次 → 正常
第二个格子:描边2次 → 稍深
第三个格子:描边3次 → 更深
第100个格子:描边100次 → 非常深
九、总结
| 问题 | 答案 |
|---|---|
beginPath() 是什么? | 开始新路径的方法 |
| 为什么要用? | 避免路径堆积,确保每个图形独立 |
| 不用会怎样? | 路径会不断累积,图形被重复绘制 |
| 在棋盘中作用? | 保证100个小方格每个只描边一次 |
| 类比 | 抬起笔,移动到新位置再落笔 |
一句话总结:beginPath() 就像画画时的“抬起笔”动作,让你能画出相互独立的图形,而不是把所有线条连在一起。