fill() 方法默认使用非零环绕规则填充路径,通过路径方向(顺时针+1、逆时针−1)累加环绕数,仅环绕数≠0的区域被填充,无需额外设置,可显式传入'nonzero'增强可读性。

在 HTML5 Canvas 中,fill() 方法默认使用非零环绕(non-zero winding)规则填充路径,这是 Canvas 的标准行为,无需额外设置。但要正确实现并理解其效果,关键在于路径绘制方向(顺时针/逆时针)和子路径的嵌套关系。
fill() 默认就是非零环绕规则
Canvas 2D 上下文的 fill() 方法默认采用非零环绕(winding)规则判断内部区域,而不是偶奇规则(evenodd)。这意味着:
- 每条子路径的方向(顺时针为 +1,逆时针为 −1)会影响“环绕数”累加
- 只有环绕数 ≠ 0 的区域才会被填充
- 无需调用额外 API —— 它是原生默认行为
用路径方向控制填充区域(核心技巧)
非零规则的本质是:对路径上任一点作任意射线,统计所有与路径相交的边,按方向加减计数(顺时针 +1,逆时针 −1),总和非零即填充。典型应用是“挖空”效果:
const ctx = canvas.getContext('2d');
ctx.beginPath();
<p>// 外圈:顺时针矩形(+1)
ctx.rect(50, 50, 200, 150);</p><p>// 内圈:逆时针圆形(−1),与外圈形成环绕数 = +1 + (−1) = 0 → 不填
ctx.moveTo(150, 125);
ctx.arc(150, 125, 40, 0, Math.PI * 2, true); // true 表示逆时针</p><p>ctx.fill(); // 中间圆区自动镂空立即学习“前端免费学习笔记(深入)”;
显式指定 fillRule 参数(可选,增强可读性)
虽然非零是默认值,但你可以显式传入 'nonzero' 提高代码可维护性,尤其与 'evenodd' 对比时:
-
ctx.fill('nonzero')—— 显式启用非零环绕(等同于ctx.fill()) -
ctx.fill('evenodd')—— 切换为偶奇规则(穿过奇数次才填充)
注意:fillRule 是上下文属性,也可通过 ctx.fillRule = 'nonzero' 设置,后续所有 fill() 都生效。
常见误区与验证建议
容易混淆的点:
-
closePath()不改变方向,它只是画一条线回到起点,环绕数取决于你之前用lineTo()或arc()构建的顶点顺序 - 多个不相连子路径(如两个独立矩形)各自贡献环绕数,互不影响
- 调试时可用
stroke()先描边,观察路径走向(箭头辅助工具或分步绘制)
简单验证:画一个顺时针三角形再叠加一个同位置逆时针小三角形,fill() 后中心将透明 —— 这就是非零规则在起作用。



















