canvas的绘制系统是基于路径
的,即先用代码定义路径(看不见的),再描边或者填充
不过canvas中也有两个当即绘制图形的API:strokeReact()与fillReact()(fillText()与strokeText()也能直接绘制,不过绘制的是文字)php
绘制复杂图形的方法都是基于路径
的html
本文总结了我对canvas路径与绘制实践,主要内容以下html5
Demo我会放在CodePen
之中,能够直接打开调试、预览canvas
在某一时刻,canvas之中只能有一条路径存在,Canvas规范将其称为“当前路径“(current path)。然而,这条路径却能够包含许多子路径(subpath)。而子路径,又是由两个或更多的点组成的。
——《HTML5 Canvas核心技术》浏览器
在W3C文档上找到了相关描述:W3C:11 Drawing paths to the canvasbash
The context always has a current default path. There is only one current path, it is not part of the drawing state. The current path is a path, as described above.ide
指出current path 即其文档中描述的path学习
Each object implementing the CanvasPathMethods interface has a path. A path has a list of zero or more subpaths. Each subpath consists of a list of one or more points, connected by straight or curved lines, and a flag indicating whether the subpath is closed or not.ui
连接在此:W3C:5 Building paths
为了方便描述不易混淆,此文就叫作当前路径
当前路径我用图这样理解👇
this
即当前路径 = 子路径3 + 子路径2 + 子路径1
在知道路径与子路径的概念后咱们看看有哪些经常使用的API
API或属性 | 说明 |
---|---|
beginPath() | starts a new path by emptying the list of sub-paths. |
closePath() | attempts to add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing. |
stroke() | strokes (outlines) the current or given path with the current stroke style. |
fill() | fills the current or given path with the current fillStyle. |
moveTo(x, y) | begins a new sub-path at the point specified by the given (x, y) coordinates. |
lineTo(x, y) | adds a straight line to the current sub-path by connecting the sub-path's last point to the specified (x, y) coordinates. |
strokeStyle | specifies the color, gradient, or pattern to use for the strokes (outlines) around shapes. The default is #000 (black). |
lineWidth | sets the thickness of lines. |
fillStyle | specifies the color, gradient, or pattern to use inside shapes. The default style is #000 (black). |
API说明来自MDN
由于本文总结的主要是路径与绘制,因此只列举了一些经常使用的API,更多的API诸如arc(),quadraticCurveTo()等可在MDN:Drawing shapes with canvas 发现
这里要注意的是beginPath()
与moveTo()
beginPath()
会清空掉原来的子路径,若是不清空,在屡次调用stroke()时,后面调用的stroke()会再次绘制原先的子路径
moveTo()
会开启新的子路径
ctx.beginPath();
ctx.strokeStyle = "pink";
ctx.lineWidth = 3;
ctx.moveTo(30, 10); //开始新的子路径
ctx.lineTo(30, 200); //绘制子路径
ctx.stroke();
ctx.beginPath(); //清空子路径
ctx.strokeStyle = "green";
ctx.moveTo(30, 200);//开始新的子路径
ctx.lineTo(230, 200);//绘制子路径
ctx.stroke();
复制代码
在第二次
调用beginPath()时清空了子路径
,并开始画绿线
。若是不清空子路径会发生什么呢,看Demo2
ctx.beginPath();
ctx.strokeStyle = "pink";
ctx.lineWidth = 3;
ctx.moveTo(30, 10);
ctx.lineTo(30, 200); //绘制子路径
ctx.stroke();
//ctx.beginPath();
ctx.strokeStyle = "green";
ctx.moveTo(30, 200);
ctx.lineTo(230, 200);//绘制子路径
ctx.stroke();
复制代码
能够看到,粉色线
不见了,实际上是被绿线覆盖了
。由于原先的子路径并无被清除,因此第二次调用stroke()
时,图中有两条子路径,故绘制时把原来的粉线覆盖。
到这里大概能够理解路径与子路径及beginPath()的做用了吧
路径分为封闭路径
与开放路径
,可是不管是封闭路径仍是开放路径均可以进行填充,当填充开放路径
时,浏览器会将其看成封闭路径来填充
浏览器自动填充Demo👇:
ctx.beginPath();
ctx.strokeStyle = "pink";
ctx.lineWidth = 5;
ctx.moveTo(30, 10);
ctx.lineTo(30, 200);
ctx.lineTo(230, 200);//绘制子路径
// ctx.closePath();
ctx.stroke();
ctx.fill();
复制代码
能够看到没有粉色斜边被stroke()
,但fill()时浏览器进行了自动填充
若是是closePath()或者手动lineTo()就会有对应的边
ctx.beginPath();
ctx.strokeStyle = "pink";
ctx.lineWidth = 5;
ctx.moveTo(30, 10);
ctx.lineTo(30, 200);
ctx.lineTo(230, 200);//绘制子路径
// ctx.closePath()或者lineTo()
// ctx.closePath()
ctx.lineTo(30,10);
ctx.stroke();
ctx.fill();
复制代码
经过上面实践(Demo2),咱们发现不能在当前路径中绘制不一样颜色的边
,同时beginPath()会清空子路径以重置当前路径,因此咱们没办法在不覆盖边
的状况下一鼓作气fill()一个三角形,须要调用四次beginPath()(三次用于画边,一次用于三角形路径并填充;若是在能够覆盖边
的状况下,三次beginPath()便可,在一次fill()时绘制边,后面绘制边时进行覆盖)
// 画粉边框
ctx.beginPath();
ctx.strokeStyle = "pink";
ctx.moveTo(30, 10);
ctx.lineTo(30, 200);
ctx.stroke();
//画绿边框
ctx.beginPath();
ctx.strokeStyle = "green";
ctx.moveTo(30, 200);
ctx.lineTo(210, 200);//绘制子路径
ctx.stroke();
//画蓝边框
ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.moveTo(210, 200);
ctx.lineTo(30, 10);//绘制子路径
ctx.stroke();
//三角形路径绘制,并填充
ctx.beginPath();
ctx.fillStyle="yellow";
ctx.moveTo(30, 10);//开始一条子路径
ctx.lineTo(30, 200);
ctx.lineTo(210, 200);
ctx.closePath();
ctx.fill();
复制代码
若是当前路径有链接造成多个闭环或者各个子路径相交造成多个闭环,在进行fill()填充时,会使用”非零环绕规则“
来判断如何进行填充
”非零环绕规则“是这么来判断有自我交叉状况的路径的:对于路径中的任意给定区域,从该区域内部画一条足够长的线段,使此线段的终点彻底落在路径范围以外。接下来,将计数器初始化为0,而后,每当这条线段与路径上的直线或曲线相交时,就改变计数器的值。若是是与路径的顺时针部分相交,则加1,若是是与路径的逆时针部分相交,则减一。若计数器的最终值不是0,那么此区域就在路径里面,在调用fill()方法时,浏览器就会对其进行填充。若是最终值是0,那么此区域就不在路径内部,浏览器也就不会对其进行填充了 ——《HTML5 Canvas核心技术》
下面举个例子,图自Wikipedia :
区域1
向外画箭头,通过一个逆时针的路径,计数器-1,再通过一个顺时针的路径,计数器+1,最终为0,因此区域1不填充
区域2
向外画箭头,通过一个顺时针的路径,计数器+1,又通过一个顺时针的路径,计数器+1,最终为2,不为0,因此区域2填充
本文经过一些基础的API讲述了路径与绘制,还有其余一些诸如arc()等创造子路径的API能够去MDN查看。
若有不对欢迎交流学习✨
HTML5 Canvas path tutorial
MDN:Drawing shapes with canvas
W3C:5 Building paths
W3C:11 Drawing paths to the canvas