Skip to content

Commit 2749573

Browse files
committed
feat: 非零环绕规则/奇偶环绕规则
1 parent dddc5bb commit 2749573

File tree

2 files changed

+72
-7
lines changed

2 files changed

+72
-7
lines changed

Diff for: docs/binary.mdx

+25
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ typedArray1[1] === 0 // true
232232
```
233233
:::
234234

235+
236+
237+
238+
235239
### 字符集
236240
上一节介绍了编码和解码,一个字符通过不同的编码方式可以转换为不同格式的二进制,而除了编码的概念外我们可能还听说过Unicode和字符集。简单来说,每个字符都和对应的码点(CodePoint)存在一对一映射关系,而根据我们采用的编码方式的不同(`utf-8``utf-16``utf-32`),同一个码点又会对应不同的二进制数据。
237241

@@ -240,6 +244,27 @@ typedArray1[1] === 0 // true
240244
```
241245

242246

247+
248+
### HTML实体字符(character Entities)
249+
250+
``` html
251+
<span>&ZeroWidthSpace;</span>
252+
<span>&#8203;</span> <!-- unicode的十进制表示-->
253+
```
254+
255+
256+
257+
### JavaScript字符表示
258+
259+
``` js
260+
// 零宽字符 zeroWidthSpace
261+
const str = '\u200B' // unicode的十六进制表示
262+
```
263+
264+
265+
266+
267+
243268
### TextEncoder
244269

245270
浏览器环境下通过`TextEncoder`,可以将字符串通过`utf-8`的格式编码成二进制(返回的是Typed Array,我们可以拿到对应的buffer)

Diff for: docs/canvas/canvas.mdx

+47-7
Original file line numberDiff line numberDiff line change
@@ -223,25 +223,65 @@ const ctx = canvas.getContext('2d', {
223223

224224
Canvas提供了`globalCompositeOperation`属性来实现混合模式。
225225

226+
227+
226228
### 裁剪Clip(Mask)
227229

228-
在路径绘制一节中我们介绍了如果创建路径,并通过`fill()`或者`stroke()`来绘制路径,除了这两个方法外我们还可以使用`clip()`来创建一个裁剪路径,后续的绘制命令都只会绘制到裁剪路径所圈出的范围内。通过这个方法可以实现遮罩Mask的效果。
230+
在路径绘制一节中我们介绍了如何创建路径,并通过`fill()`或者`stroke()`来填充路径或描边路径,除了这两个方法外我们还可以使用`clip()`来创建一个裁剪路径。后续的绘制命令都只会绘制到裁剪路径所圈出的**范围内**通过这个方法可以实现遮罩Mask的效果。
229231

230232
``` js
231233
ctx.save()
232234
ctx.beginPath();
233-
ctx.moveTo(0, 0)
234-
ctx.lineTo(100, 0)
235-
ctx.lineTo(0, 100)
235+
ctx.arc(100, 100, 50, 0, Math.PI * 2)
236236
ctx.clip();
237-
238237
ctx.fillStyle = 'pink'
239-
ctx.fillRect(0, 0, 200, 200)
238+
ctx.fillRect(0, 0, 100, 100)
239+
ctx.restore() // 不恢复的话后续其他绘制都只能在clip区执行了
240+
241+
ctx.fillStyle = 'skyblue'
242+
ctx.fillRect(100, 100, 100, 100)
243+
```
244+
245+
在这个例子中我们创建了一个圆形的裁剪路径,后续绘制的粉色矩形会被裁剪到只展示圆形裁剪路径内的内容。
246+
247+
248+
249+
#### 路径环绕规则
250+
251+
通过上面的例子,我们知道在Canvas中通过裁剪来实现遮罩效果是很简单的;我们可以更进一步,思考一下如何实现**反向的裁剪区域**,即只绘制圆形裁剪路径之外的内容?
252+
253+
Canvas的`fill`API能够填充路径的**内部区域**`clip`能够把路径的**内部区域**视为裁剪区域。当一个路径包含多个区域时,我们怎么分辨某个区域是属于路径的内部还是外部呢?这是通过内部的**路径环绕规则**来决定的,`fill``clip`这两个API都支持传入参数来指定路径环绕规则,分别是默认的**非零环绕规则nonzero**以及**奇偶环绕规则evenodd**。在图形学中,这个规则可以用来判断一个点是否在多边形(路径)的内部来进行点击/碰撞计算。
254+
240255

256+
257+
**非零环绕规则nonzero**
258+
259+
Canvas默认使用非零环绕规则。简单来说,对于区域内的任意点向外无限远引一条射线,射线会经过若干条路径,假如其中两条路径是顺时针环绕的,另一条路径是逆时针环绕的,两种环绕的差值不为零,那么说明这个区域是在**路径内部的**。当使用`fill`时,这个区域会被填充;当使用`clip`时,这个区域会被视为裁剪区域。
260+
261+
262+
263+
**奇偶环绕规则evenodd**
264+
265+
与非零环绕规则不同的是,奇偶环绕规则无视了路径的环绕方向(顺时针或逆时针)。对于区域内的任意点向外无限远引一条射线,射线如果总是经过奇数条路径,则该区域在路径内部;否则区域在路径外部。
266+
267+
268+
269+
我们现在了解了`clip`默认使用的非零环绕规则的原理,那么如何实现我们最初的目标“反向裁剪”?事实上,我们可以先绘制一个顺时针方向的矩形,再在内部绘制一个逆时针反向的圆形,这样通过非零环绕规则的计算这两个图形中间的区域会被视为路径的内部,成为了裁剪区域;而圆形内部的区域,则会被视为路径的外部,不会再被视为裁剪区域。
270+
271+
``` js
272+
const ctx = canvas.getContext('2d');
273+
ctx.save()
274+
ctx.beginPath();
275+
276+
ctx.rect(0, 0, 200, 200) // 先顺时针绘制矩形
277+
ctx.arc(100, 100, 50, 0, Math.PI * 2, true) // 通过传入true来逆时针绘制圆形
278+
ctx.clip();
279+
ctx.fillStyle = 'pink'
280+
ctx.fillRect(0, 0, 100, 100)
241281
ctx.restore() // 不恢复的话后续其他绘制都只能在clip区执行了
242282

243283
ctx.fillStyle = 'skyblue'
244-
ctx.fillRect(50, 50, 200, 200)
284+
ctx.fillRect(100, 100, 100, 100)
245285
```
246286

247287

0 commit comments

Comments
 (0)