這篇將要介紹 Canvas 中的基本圖形。
圖形的基礎 - 路徑
在 Canvas 中,所有基本圖形都是以路徑為基礎的,也就是說,我們在調用 2dContext 的 lineTo、rect 等方法時,其實就是往已的 context 再加上一些路徑點,在最後使用 fill 或 stroke 方法進行繪製時,都是依據這些路徑點來進行填入或畫線。
在每次開始繪製路徑前,都應該使用 context.beginPath() 方法來告訴 Context 物件開始繪製一條新的路徑,否則接下來繪製的路徑會與先前繪製的路徑疊加,在填充或畫邊框時就會出現問題。在繪製完成路徑後,可以直接使用 context.closePath() 方法關閉路徑,或手動關閉路徑。另外,如果在填滿時路徑沒有關閉,那麼 Context 會自動呼叫 closePath 方法將路徑關閉。
基本路徑方法
1. beginPath, closePath
這兩個方法在前面已經介紹過,分別用來通知 Context 開始一個新的路徑和關閉目前的路徑。
在 Canvas 中使用路徑時,應該要保持一個良好的習慣,每次開始繪製路徑前都要調用一次 beginPath 方法,否則畫出來的效果難看不說,還會嚴重影響性能。
在下面這張圖中,左邊的圖形在每次繪製矩形前都調用了一次 beginPath 來清除之前的路徑並重新開始繪製新的路徑,而後面的圖形則只在繪製所有圖形前調用了一次 beginPath來清除路徑,因此,雖然這裡是使用的邊框色是 #666,但是右邊的圖形顏色比左邊的深一些,因為每次使用 stroke 繪製邊框時,會把之前的路徑再次繪製一遍,疊加起來顏色就比原來深一些。
<canvas id="canvas" width="500" style="max-width:90%"></canvas> <script type="text/javascript"> var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.strokeStyle = "#666"; function useBeginPath() { for (var i = 0; i < 5; ++i) { ctx.beginPath(); ctx.rect(10 + i*20, 10 + i*20, 210 - i*40, 210 - i*40); ctx.stroke(); } } function notUseBeginPath() { ctx.beginPath(); for (var i = 0; i < 5; ++i) { ctx.rect(240 + i*20, 10 + i*20, 210 - i*40, 210 - i*40); ctx.stroke(); } } useBeginPath(); notUseBeginPath(); </script>
在 Context 中路徑數較少時,如果不考慮顯示效果,性能上還可以接受,但是如果 Context 中的路徑數很多時,在開始繪製新路徑前不使用 beginPath次繪製都要將先前的路徑重新繪製一遍,這時效能會以指數下降。
因此,除非有特殊需要,每次開始繪製路徑前都要呼叫 beginPath 來開始新路徑。
2. 移動與直線 moveTo, lineTo, rect
<canvas id="canvas" width="500" style="max-width:90%"></canvas> <script type="text/javascript"> var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(110,110); ctx.lineTo(10, 110); ctx.lineTo(10, 10); ctx.stroke(); ctx.beginPath(); ctx.rect(120, 10, 100, 100); ctx.stroke(); </script>
void moveTo(in float x, in Canfloat y);上一次繪製路徑的終點,因此,如果需要指定起點的話,就需要使用 moveTo 方法來指定要移動到的位置。
void lineTo(in float x, in float y);
lineTo 方法則是繪製一條直接路徑到指定的位置。在調用完 lineTo 方法後,Context 內部的繪圖起點會移到直線的終點。
void rect(in float x, in float y, in float w, in float h);
rect 方法用來繪製一個矩形路徑,並透過參數指定左上角位置以及寬和高。在呼叫 rect 後,Context 的繪圖起點會移到 rect 所繪製的矩形的左上角。
rect 方法與後面要介紹的 arc 方法與其他路徑方法有一點不同,它們是使用參數指定起點的,而不是使用 Context 內部維護的起點。
3. 曲線 arcTo, arc, quadraticCurveTo, bezierCurveTo
void arcTo(in float x1, );
依照 WHATWG 文件的說明,這個方法是畫一個與兩條射線相切的的圓弧,兩條射線其中一條為穿過 Context 繪製起點,終點為 (x1, y1),另外一條為穿過 (x2, y2),終點為 (x1, y1) ,這條圓弧為最小的與這兩條射線相切的圓弧。調用完 arcTo 方法後,將 圓弧與 射線 (x1, y1)-(x2, y2) 的切點加到目前路徑中,並為下次繪製的起點。
在測試中發現,Firefox 和 Opera 目前對此方法的支援並不好,只有 Chrome 和 Safari 4 能畫出正確的路徑。
图中的的两条灰色直线是偏移 4 个像素后的两条射线所在的位置。
<canvas id="canvas" width="500" height="500"></canvas> <script type="text/javascript"> var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000"; ctx.translate(200, 200); ctx.moveTo(10, 10); ctx.arcTo(110, 60, 10, 110, 30); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = "#999"; ctx.moveTo(10, 6); ctx.lineTo(114, 60); ctx.lineTo(10, 114); ctx.stroke(); </script>
void arc(in float x, in float y, in float radius, in float startAngle, in float endAngle, in boolean anticlockwise);
arc 方法用来绘制一段圆弧路径,通过圆心位置、起始弧度、终止弧度来指定圆弧的位置和大小,这个方法也不依赖于 Context 维护的绘制起点。而在画圆弧时的旋转方向则由最后一个参数 anticlockwise 来指定,如果为 true 就是逆时针,false 则为顺时针。
void quadraticCurveTo(in float cpx, in float cpy, in float x, in float y);
quadraticCurveTo 方法用来绘制二次样条曲线路径,参数中 cpx 与 cpy 指定控制点的位置,x 和 y 指定终点的位置,起点则是由 Context 维护的绘制起点。
void bezierCurveTo(in float cp1x, in float cp1y, in float cp2x, in float cp2y, in float x, in float y);
bezierCurveTo 方法用来绘制贝塞尔曲线路径,它与 quadraticCurveTo 相似,不过贝塞尔曲线有两个控制点,因此参数中的 cp1x, cp1y, cp2x, cp2y 用来指定两个控制点的位置,而 x 和 y 指定绺的位置。
<canvas id="canvas" width="500" style="max-width:90%"></canvas> <script type="text/javascript"> var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.translate(10, 10); ctx.beginPath(); ctx.arc(50, 50, 50, 0, Math.PI, true); ctx.stroke(); // quadraticCurveTo ctx.beginPath(); ctx.strokeStyle = "#000"; ctx.moveTo(110, 50); ctx.quadraticCurveTo(160, 0, 210, 50); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = "red"; ctx.moveTo(110, 50); ctx.lineTo(160, 0); ctx.lineTo(210, 50); ctx.stroke(); // bezierCurveTo ctx.beginPath(); ctx.strokeStyle = "#000"; ctx.moveTo(220, 50); ctx.bezierCurveTo(250, 0, 280, 10, 320, 50); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = "red"; ctx.moveTo(220, 50); ctx.lineTo(250, 0); ctx.lineTo(280, 10); ctx.lineTo(320, 50); ctx.stroke(); </script>
4. fill, stroke, clip
fill 与 stroke 这两个方法很好理解,分别用来填充路径与绘制路径线条。
clip 方法用来给 Canvas 设置一个剪辑区域,在调用 clip 方法之后的代码只对这个设定的剪辑区域有效,不会影响其他地方,这个方法在要进行局部更新时很有用。默认情况下,剪辑区域是一个左上角在 (0, 0),宽和高分别等于 Canvas 元素的宽和高的矩形。
在画这个图时,虽然两次都是使用 fillRect(0, 0, 100, 100) 填充了一个 100x100 大小矩形,但是显示的结果却是第二次填充的只是中间的一小块,这是因为在两次填充之间使用 clip 方法设定了剪辑区域,这样第二次填充时只会影响到所设定的中间那一小部分区域。
<canvas id="canvas" width="500" height="500"></canvas> <script type="text/javascript"> var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.translate(10, 10); // fill a green rectangle ctx.fillStyle = "green"; ctx.fillRect(0, 0, 100, 100); // set the clipping region ctx.beginPath(); ctx.rect(30, 30, 40, 40); ctx.clip(); ctx.stroke(); // fill a yellow rectangle ctx.fillStyle = "yellow"; ctx.fillRect(0, 0, 100, 100); </script>
5. clearRect, fillRect, strokeRect
这三个方法并不是路径方法,而是用来直接处理 Canvas 上的内容,相当于 Canvas 的背景,调用这三个方法也不会影响 Context 绘图的起点。
要清除 Canvas 上的所有内容时,可以直接调用 context.clearRect(0, 0, width, height) 来直接清除,而不需要使用路径方法绘制一个与 Canvas 同等大小的矩形路径再使用 fill 方法去清除。
结语
通过 Canvas 的路径方法,可以使用 Canvas 处理一些简单的矢量图形,这样在缩放时也不会失真。不过 Canvas 的路径方法也不是很强大,至少连个椭圆的路径都没有……
这篇写得有点长了,Cnavas 中路径相关的内容就写这么多,后面再讲讲 Canvas 其他的东西。
以上就是HTML5 Canvas 起步(2) - 路径的内容,更多相关内容请关注PHP中文网(www.php.cn)!