SVG fill-rule 原理

SVG 的 fill-rule 属性在 规范中 有描述如下。

The fill-rule property indicates the algorithm which is to be used to determine what parts of the canvas are included inside the shape. For a simple, non-intersecting path, it is intuitively clear what region lies "inside"; however, for a more complex path, such as a path that intersects itself or where one subpath encloses another, the interpretation of "inside" is not so obvious.

大意是说 fill-rule 是用来判断某一点是否在内部的,且有 nonzeroevenodd 两个属性。方法都是在图形内选择一点,并计一个初始值 0。从选定点往任意方向做一条射线,该射线从路径左边穿过则计数加一,从右边穿过则计数减一,射线在其方向上穿过所有路径之后得到计数结果。属性值 nonzero 意思是,若计数结果为 0 则判断该点在外。而 evenodd 意思是,计数结果为偶数,则该点在外。反之判定该点在图形内。

The fill-rule property provides two options for how the inside of a shape is determined:

nonzero

This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and then examining the places where a segment of the shape crosses the ray. Starting with a count of zero, add one each time a path segment crosses the ray from left to right and subtract one each time a path segment crosses the ray from right to left. After counting the crossings, if the result is zero then the point is outside the path. Otherwise, it is inside.

evenodd

This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and counting the number of path segments from the given shape that the ray crosses. If this number is odd, the point is inside; if even, the point is outside.

示例

话不多说,上个图你就明白了。

如上图,从蓝色点印一条射线,与路径相交两次,蓝色射线在白色圈和绿色圈处分别两次从路径的右边与其相交,那此时我们得到的计数为 2。若用 fill-rule: nonezero 规则,判定蓝色点在内,但用 fill-rule: evenodd 规则,判定蓝色点在内。

fill-rule: nonezero

<svg  
  xmlns="http://www.w3.org/2000/svg" 
  width="200" 
  height="200" 
  viewBox="0 0 500 400"
>
  <path 
    d="M250 75l73 226-192-140h238L177 301z" 
    fill="#0c6" 
    stroke="#000" 
    stroke-width="3"
    fill-rule="nonezero"
  ></path>
</svg>  

fill-rule: evenodd

<svg  
  xmlns="http://www.w3.org/2000/svg" 
  width="200" 
  height="200" 
  viewBox="0 0 500 400"
>
  <path 
    d="M250 75l73 226-192-140h238L177 301z" 
    fill="#0c6" 
    stroke="#000" 
    stroke-width="3"
    fill-rule="evenodd"
  ></path>
</svg>