「用 Canvas 畫圖」修訂間的差異
出自 MozTW Wiki
Danielwang(對話 | 貢獻) (→使用路徑) |
Danielwang(對話 | 貢獻) (→較複雜的範例) |
||
行 6: | 行 6: | ||
Mozilla Firefox 1.5 裡有個新的,作用為可編程圖片的 HTML 元素。<code><canvas></code> 根據 [http://www.whatwg.org/specs/web-apps/current-work/#graphics WhatWG canvas 規格],而這又是根據 Apple Safari 裡的 <code><canvas></code> 實作。這元素可用來利用程式碼在客戶端上繪製圖表、介面、或其它自訂圖片。 | Mozilla Firefox 1.5 裡有個新的,作用為可編程圖片的 HTML 元素。<code><canvas></code> 根據 [http://www.whatwg.org/specs/web-apps/current-work/#graphics WhatWG canvas 規格],而這又是根據 Apple Safari 裡的 <code><canvas></code> 實作。這元素可用來利用程式碼在客戶端上繪製圖表、介面、或其它自訂圖片。 | ||
− | <code><canvas></code> 會建立固定大小的繪圖空間,並會有一個或多個「繪製態」(rendering context)。在本文裡我們會專注在 2D | + | <code><canvas></code> 會建立固定大小的繪圖空間,並會有一個或多個「繪製態」(rendering context)。在本文裡我們會專注在 2D 的繪製(這也是目前唯一有定義的 context)。在未來將會更多的 context 來提供其它類別的繪製,例如使用 OpenGL 的 3D 繪製。 |
− | + | == 2D Context(平面繪製態) == | |
− | |||
− | == 2D | ||
=== 簡單的範例 === | === 簡單的範例 === | ||
行 38: | 行 36: | ||
</html> | </html> | ||
− | <code>draw</code> 函數先取得 <code>canvas</code> 元素,再取得 <code>2d</code> | + | <code>draw</code> 函數先取得 <code>canvas</code> 元素,再取得 <code>2d</code> context。<code>ctx</code> 這物件可用來實際在畫板上繪製。本範例以用 CSS 顏色設定 fillStyle(填滿樣式)與呼叫 <code>fillRect</code> 的方式來繪出兩個方形。第二個 fillStyle 是用 <code>rgba()</code> 來一併定義顏色與透度(alpha)的值。 |
<code>fillRect</code>、<code>strokeRect</code>、與 <code>clearRect</code> calls 繪製填充、輪廓線、與透明的長方形。較複雜的形狀用路徑(path)。 | <code>fillRect</code>、<code>strokeRect</code>、與 <code>clearRect</code> calls 繪製填充、輪廓線、與透明的長方形。較複雜的形狀用路徑(path)。 | ||
行 56: | 行 54: | ||
ctx.beginPath(); // 開始路徑 | ctx.beginPath(); // 開始路徑 | ||
− | ctx.moveTo(30, 30); // | + | ctx.moveTo(30, 30); // (起點)移到 30, 30 |
− | ctx.lineTo(150, 150); // | + | ctx.lineTo(150, 150); // 做直線到 150, 150 |
− | ctx.quadraticCurveTo(60, 70, 70, 150); // | + | ctx.quadraticCurveTo(60, 70, 70, 150); // 做一元二次方程曲線到 70, 150 |
− | ctx.lineTo(30, 30); // | + | ctx.lineTo(30, 30); // 做直線到 30, 30 |
− | ctx.fill(); // | + | ctx.fill(); // 路徑內填色 |
} | } | ||
</script> | </script> | ||
行 70: | 行 68: | ||
呼叫 <code>fill()</code> 或 <code>stroke()</code> 會使得目前的路徑被用掉。要再塗滿或畫線的話,路徑必須要重建一次。 | 呼叫 <code>fill()</code> 或 <code>stroke()</code> 會使得目前的路徑被用掉。要再塗滿或畫線的話,路徑必須要重建一次。 | ||
+ | |||
+ | === 圖片狀態 === | ||
+ | |||
+ | Context 的如 <code>fillStyle</code>、<code>strokeStyle</code>、<code>lineWidth</code>、與 <code>lineJoin</code> 的屬性屬於 | ||
+ | 目前的圖片狀態(graphics state)的一部份。Context 有 <code>save()</code>(儲存)與 <code>restore()</code>(返回)兩個函數讓目前的狀態可以存入狀態堆疊(stack)與從中移除。(注意路徑不屬於圖片狀態。) | ||
+ | |||
+ | === 較複雜的範例 === | ||
+ | |||
+ | 現在來一個複雜點的範例。這範例用到路徑(paths)與狀態(state),並且它也介紹了目前轉換矩陣(current transformation matrix)的概念。Context 的 | ||
+ | <code>translate()</code>(移動)、<code>scale()</code>(縮放),與 <code>rotate()</code>(轉動)方式會轉換目前的矩陣。所有繪製的點都會先用這個矩陣。 | ||
+ | |||
+ | [[Image:canvas_ex3.png|right|frame|範例三]] | ||
+ | <pre> | ||
+ | <html> | ||
+ | <head> | ||
+ | <script type="application/x-javascript"> | ||
+ | |||
+ | // 畫蝴蝶結 | ||
+ | function drawBowtie(ctx, fillStyle) { | ||
+ | |||
+ | ctx.fillStyle = "rgba(200,200,200,0.3)"; // 把「填滿樣式」設為灰色,0.3 透度 | ||
+ | ctx.fillRect(-30, -30, 60, 60); // 畫一個填色的方形 | ||
+ | |||
+ | ctx.fillStyle = fillStyle; // 設定填色 | ||
+ | |||
+ | ctx.globalAlpha = 1.0; // 全域透度 | ||
+ | ctx.beginPath(); // 開始路徑 | ||
+ | ctx.moveTo(25, 25); // (起點)移到 25, 25 | ||
+ | ctx.lineTo(-25, -25); // 做直線到 -25, -25 | ||
+ | ctx.lineTo(25, -25); // 做直線到 25, -25 | ||
+ | ctx.lineTo(-25, 25); // 做直線到 -25, 25 | ||
+ | ctx.closePath(); // 關閉路徑 | ||
+ | ctx.fill(); // 填色 | ||
+ | } | ||
+ | |||
+ | // 畫點 | ||
+ | function dot(ctx) { | ||
+ | ctx.save(); // 儲存目前狀態 | ||
+ | ctx.fillStyle = "black"; // 填色樣式設為黑色 | ||
+ | ctx.fillRect(-2, -2, 4, 4); // 畫黑色正方形 | ||
+ | ctx.restore(); // 返回狀態 | ||
+ | } | ||
+ | |||
+ | function draw() { | ||
+ | var canvas = document.getElementById("canvas"); | ||
+ | var ctx = canvas.getContext("2d"); | ||
+ | |||
+ | // 注意其它的移動都是相對於這一個 | ||
+ | ctx.translate(45, 45); | ||
+ | |||
+ | ctx.save(); | ||
+ | //ctx.translate(0, 0); // 不需要 | ||
+ | drawBowtie(ctx, "red"); // 畫綠色蝴蝶結 | ||
+ | dot(ctx); | ||
+ | ctx.restore(); | ||
+ | |||
+ | ctx.save(); // 儲存目前狀態 | ||
+ | ctx.translate(85, 0); // 移動(向右 85) | ||
+ | ctx.rotate(45 * Math.PI / 180); // 轉動 45 度 | ||
+ | drawBowtie(ctx, "green"); // 畫綠色蝴蝶結 | ||
+ | dot(ctx); // 畫中心點 | ||
+ | ctx.restore(); // 返回到先前的狀態 (轉動 0, 移動 45, 45) | ||
+ | |||
+ | ctx.save(); // 儲存目前狀態 | ||
+ | ctx.translate(0, 85); // 移動(向下 85) | ||
+ | ctx.rotate(135 * Math.PI / 180); // 轉動 135 度 | ||
+ | drawBowtie(ctx, "blue"); // 畫籃色蝴蝶結 | ||
+ | dot(ctx); // 畫中心點 | ||
+ | ctx.restore(); // 返回到先前的狀態 (轉動 0, 移動 45, 45) | ||
+ | |||
+ | ctx.save(); | ||
+ | ctx.translate(85, 85); | ||
+ | ctx.rotate(90 * Math.PI / 180); | ||
+ | drawBowtie(ctx, "yellow"); // 畫黃色蝴蝶結 | ||
+ | dot(ctx); | ||
+ | ctx.restore(); | ||
+ | } | ||
+ | </script> | ||
+ | </head> | ||
+ | <body onload="draw()"> | ||
+ | <canvas id="canvas" width="300" height="300"></canvas> | ||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | |||
+ | 這裡有定義兩個函數 <code>drawBowtie</code> 與 <code>dot</code>,且各被呼叫四次。每次呼叫前 <code>translate()</code> 與 <code>rotate()</code> 被用來設立 current transformation matrix(目前轉換矩陣)以定位中心點和蝴蝶結。<code>dot</code> 繪製一個中心在 <code>(0, 0)</code> 的黑方形。這個點用轉換矩陣來移位。<code>drawBowtie</code> 用收到的填滿樣式繪製一個蝴蝶結。 | ||
+ | |||
+ | 因為矩陣運算有累積,每一段 <code>save()</code> 和 <code>restore()</code> 被用來返回到原來的 canvas 狀態。需注意的一點的是轉動永遠是繞著目前的原點,因此 <code>translate() rotate() translate()</code> 的呼叫的結果會與 code>translate() translate() rotate()</code> 一串的有所不同。 | ||
+ | |||
+ | == 與 Apple <canvas> 的相容性 == | ||
+ | |||
+ | === 要求的 <code></canvas></code> 標籤 === | ||
+ | |||
+ | == 其它的功能 == | ||
+ | |||
+ | === 把網頁內容畫到 Canvas 內 === | ||
+ | |||
+ | == 參見 == | ||
+ | * [http://developer.mozilla.org/en/docs/Canvas_tutorial Canvas tutorial] | ||
+ | * [http://developer.mozilla.org/en/docs/A_Basic_RayCaster A Basic RayCaster] | ||
+ | * [http://developer.apple.com/documentation/AppleApplications/Reference/SafariJSRef/Classes/Canvas.html Apple Canvas Documentation] | ||
+ | * [http://weblogs.mozillazine.org/roc/archives/2005/05/rendering_web_p.html Rendering Web Page Thumbnails] | ||
+ | * [http://awordlike.com/ The Lightweight Visual Thesaurus] |
於 2005年10月23日 (日) 23:13 的修訂
「用 Canvas 畫圖」尚未編寫完成。你現在就可以編修本頁面,擴充本文內容。
內容大綱
簡介
Mozilla Firefox 1.5 裡有個新的,作用為可編程圖片的 HTML 元素。<canvas>
根據 WhatWG canvas 規格,而這又是根據 Apple Safari 裡的 <canvas>
實作。這元素可用來利用程式碼在客戶端上繪製圖表、介面、或其它自訂圖片。
<canvas>
會建立固定大小的繪圖空間,並會有一個或多個「繪製態」(rendering context)。在本文裡我們會專注在 2D 的繪製(這也是目前唯一有定義的 context)。在未來將會更多的 context 來提供其它類別的繪製,例如使用 OpenGL 的 3D 繪製。
2D Context(平面繪製態)
簡單的範例
我們一開始先來一個簡單的,一個半透明正方形蓋在另一個正方形之上的範例:
<html> <head> <script type="application/x-javascript"> function draw() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.fillStyle = "rgb(200,0,0)"; // 把「填滿樣式」設為紅 200 綠 0 籃 0 ctx.fillRect (10, 10, 50, 50); // 畫一個填充的長方形 ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; // 把「填滿樣式」設為紅 0 綠 0 籃 200 透度 0.5 ctx.fillRect (30, 30, 50, 50); // 畫一個填充的長方形 } </script> </head> <body onload="draw()"> <canvas id="canvas" width="300" height="300"></canvas> </body> </html>
draw
函數先取得 canvas
元素,再取得 2d
context。ctx
這物件可用來實際在畫板上繪製。本範例以用 CSS 顏色設定 fillStyle(填滿樣式)與呼叫 fillRect
的方式來繪出兩個方形。第二個 fillStyle 是用 rgba()
來一併定義顏色與透度(alpha)的值。
fillRect
、strokeRect
、與 clearRect
calls 繪製填充、輪廓線、與透明的長方形。較複雜的形狀用路徑(path)。
使用路徑
beginPath
函數開始一個新的路徑,moveTo
、lineTo
、arcTo
、arc
、與類似的函數加入路徑片段。路徑可用 closePath
把頭尾連起來。建立路徑之後你可以用 fill
或 stroke
來把路徑繪製到 canvas 上。
<html> <head> <script type="application/x-javascript"> function draw() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.fillStyle = "red"; ctx.beginPath(); // 開始路徑 ctx.moveTo(30, 30); // (起點)移到 30, 30 ctx.lineTo(150, 150); // 做直線到 150, 150 ctx.quadraticCurveTo(60, 70, 70, 150); // 做一元二次方程曲線到 70, 150 ctx.lineTo(30, 30); // 做直線到 30, 30 ctx.fill(); // 路徑內填色 } </script> </head> <body onload="draw()"> <canvas id="canvas" width="300" height="300"></canvas> </body> </html>
呼叫 fill()
或 stroke()
會使得目前的路徑被用掉。要再塗滿或畫線的話,路徑必須要重建一次。
圖片狀態
Context 的如 fillStyle
、strokeStyle
、lineWidth
、與 lineJoin
的屬性屬於
目前的圖片狀態(graphics state)的一部份。Context 有 save()
(儲存)與 restore()
(返回)兩個函數讓目前的狀態可以存入狀態堆疊(stack)與從中移除。(注意路徑不屬於圖片狀態。)
較複雜的範例
現在來一個複雜點的範例。這範例用到路徑(paths)與狀態(state),並且它也介紹了目前轉換矩陣(current transformation matrix)的概念。Context 的
translate()
(移動)、scale()
(縮放),與 rotate()
(轉動)方式會轉換目前的矩陣。所有繪製的點都會先用這個矩陣。
<html> <head> <script type="application/x-javascript"> // 畫蝴蝶結 function drawBowtie(ctx, fillStyle) { ctx.fillStyle = "rgba(200,200,200,0.3)"; // 把「填滿樣式」設為灰色,0.3 透度 ctx.fillRect(-30, -30, 60, 60); // 畫一個填色的方形 ctx.fillStyle = fillStyle; // 設定填色 ctx.globalAlpha = 1.0; // 全域透度 ctx.beginPath(); // 開始路徑 ctx.moveTo(25, 25); // (起點)移到 25, 25 ctx.lineTo(-25, -25); // 做直線到 -25, -25 ctx.lineTo(25, -25); // 做直線到 25, -25 ctx.lineTo(-25, 25); // 做直線到 -25, 25 ctx.closePath(); // 關閉路徑 ctx.fill(); // 填色 } // 畫點 function dot(ctx) { ctx.save(); // 儲存目前狀態 ctx.fillStyle = "black"; // 填色樣式設為黑色 ctx.fillRect(-2, -2, 4, 4); // 畫黑色正方形 ctx.restore(); // 返回狀態 } function draw() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); // 注意其它的移動都是相對於這一個 ctx.translate(45, 45); ctx.save(); //ctx.translate(0, 0); // 不需要 drawBowtie(ctx, "red"); // 畫綠色蝴蝶結 dot(ctx); ctx.restore(); ctx.save(); // 儲存目前狀態 ctx.translate(85, 0); // 移動(向右 85) ctx.rotate(45 * Math.PI / 180); // 轉動 45 度 drawBowtie(ctx, "green"); // 畫綠色蝴蝶結 dot(ctx); // 畫中心點 ctx.restore(); // 返回到先前的狀態 (轉動 0, 移動 45, 45) ctx.save(); // 儲存目前狀態 ctx.translate(0, 85); // 移動(向下 85) ctx.rotate(135 * Math.PI / 180); // 轉動 135 度 drawBowtie(ctx, "blue"); // 畫籃色蝴蝶結 dot(ctx); // 畫中心點 ctx.restore(); // 返回到先前的狀態 (轉動 0, 移動 45, 45) ctx.save(); ctx.translate(85, 85); ctx.rotate(90 * Math.PI / 180); drawBowtie(ctx, "yellow"); // 畫黃色蝴蝶結 dot(ctx); ctx.restore(); } </script> </head> <body onload="draw()"> <canvas id="canvas" width="300" height="300"></canvas> </body> </html>
這裡有定義兩個函數 drawBowtie
與 dot
,且各被呼叫四次。每次呼叫前 translate()
與 rotate()
被用來設立 current transformation matrix(目前轉換矩陣)以定位中心點和蝴蝶結。dot
繪製一個中心在 (0, 0)
的黑方形。這個點用轉換矩陣來移位。drawBowtie
用收到的填滿樣式繪製一個蝴蝶結。
因為矩陣運算有累積,每一段 save()
和 restore()
被用來返回到原來的 canvas 狀態。需注意的一點的是轉動永遠是繞著目前的原點,因此 translate() rotate() translate()
的呼叫的結果會與 code>translate() translate() rotate() 一串的有所不同。