「用 Canvas 畫圖」修訂間的差異
出自 MozTW Wiki
Danielwang(對話 | 貢獻) 小 |
小 (復原由Cthouse002 (對話)的編輯;更改回Xacid的最後一個版本) |
||
(未顯示由 5 位使用者於中間所作的 11 次修訂) | |||
行 1: | 行 1: | ||
− | {{翻譯:mdc | Drawing_Graphics_with_Canvas | Drawing Graphics with Canvas | + | {{翻譯:mdc|Drawing_Graphics_with_Canvas|Drawing Graphics with Canvas}} |
− | |||
== 簡介 == | == 簡介 == | ||
− | Mozilla Firefox 1.5 | + | 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> | + | <code><canvas></code> 會建立固定大小的繪圖空間,並會有一個或多個「繪製環境」(rendering context)。在本文裡我們會專注在 2D 的繪製上(這也是目前唯一有定義的 context)。在未來會有更多的 context 提供其它類別的繪製,例如使用 OpenGL 的 3D 繪製。 |
− | + | == 2D Context(平面繪製環境) == | |
− | |||
− | == 2D | ||
=== 簡單的範例 === | === 簡單的範例 === | ||
− | + | 一開始我們先來做一個簡單的練習,底下是一個半透明正方形蓋在另一個正方形之上的範例: | |
[[Image:canvas_ex1.png|right|frame|範例一]] | [[Image:canvas_ex1.png|right|frame|範例一]] | ||
行 25: | 行 22: | ||
var ctx = canvas.getContext("2d"); | var ctx = canvas.getContext("2d"); | ||
− | ctx.fillStyle = "rgb(200,0,0)"; // 把「填滿樣式」設為紅 200 綠 0 | + | ctx.fillStyle = "rgb(200,0,0)"; // 把「填滿樣式」設為紅 200 綠 0 藍 0 |
ctx.fillRect (10, 10, 50, 50); // 畫一個填充的長方形 | ctx.fillRect (10, 10, 50, 50); // 畫一個填充的長方形 | ||
− | ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; // 把「填滿樣式」設為紅 0 綠 0 | + | ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; // 把「填滿樣式」設為紅 0 綠 0 藍 200 透度 0.5 |
ctx.fillRect (30, 30, 50, 50); // 畫一個填充的長方形 | ctx.fillRect (30, 30, 50, 50); // 畫一個填充的長方形 | ||
} | } | ||
行 38: | 行 35: | ||
</html> | </html> | ||
− | <code>draw</code> 函數先取得 <code>canvas</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)。 | ||
+ | |||
+ | === 使用路徑 === | ||
+ | |||
+ | <code>beginPath</code> 函數可以開始一個新的路徑,<code>moveTo</code>、<code>lineTo</code>、<code>arcTo</code>、<code>arc</code>、或其他類似的函數可以加入路徑片段。路徑可用 <code>closePath</code> 把頭尾連起來。建立路徑之後你可以用 <code>fill</code> 或 <code>stroke</code> 來把路徑繪製到 canvas 上。 | ||
+ | |||
+ | [[Image:canvas_ex2.png|right|frame|範例二]] | ||
+ | <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> | ||
+ | |||
+ | 呼叫 <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)與從中移除。(注意路徑不屬於圖片狀態。) | ||
+ | |||
+ | === 較複雜的範例 === | ||
+ | |||
+ | 現在來一個複雜一點的範例。這範例用到路徑(path)與狀態(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> 的實作大致上與 Apple 的相容。不過有些小地方需要注意一下。 | ||
+ | |||
+ | === 要求的 <code></canvas></code> 標籤 === | ||
+ | |||
+ | <code><canvas></code> 在 Apple Safari 的實作裡與 <code><img></code> 相似,不需要結束標籤。但是要讓 <code><canvas></code> 在網路上能有更廣泛的用途,必須要有備用環境(fallback content)的機制。因此 Mozilla 的實作有要求結束標籤。 | ||
+ | |||
+ | 如果不需要備用環境,簡單的 <code><canvas id="foo" ...></canvas></code> 會與 Safari 和 Mozilla 完全相容 — Safari 會忽略那個結束標籤。 | ||
+ | |||
+ | 若是有備用環境,則必須要用到一些 CSS 技巧讓備用環境不會在 Safari 裡出現,但會出現在 Internet Explorer 裡。 | ||
+ | <!-- | ||
+ | 如果倒退內容有需要,some CSS tricks must be employed to mask the fallback content from Safari (which should render just the canvas), and also to mask the CSS tricks themselves from IE (which should render the fallback content). | ||
+ | --> | ||
+ | |||
+ | == 其它的功能 == | ||
+ | |||
+ | === 把網頁內容畫到 Canvas 內 === | ||
+ | |||
+ | Mozilla 的 <code>canvas</code> 有用 <code>drawWindow</code> 延伸。這個函數把一個 DOM 視窗內容的快照的畫入 canvas 內。舉例來說 | ||
+ | ctx.drawWindow(window, 0, 0, 100, 200, "rgb(0,0,0)"); | ||
+ | 會把目前視窗的內容中,相對於檢視區左上角的 rectangle (0,0,100,200) 的部份,用黑色背景畫到 canvas 內。顏色設定如用 "rgba(0,0,0,0)" 會使內容有透明背景(但會較慢)。 | ||
+ | |||
+ | 有了這個方法,我們可以做一個隱藏式的 iframe,放入任何內容,然後畫到 canvas 中。這圖片會照目前的轉換(transformation)來縮放與轉動等。 | ||
+ | |||
+ | == 參見 == | ||
+ | * [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] | ||
+ | |||
+ | {{link2|技術傳教專案主頁|C|萬維網設計|HTML}} |
於 2012年2月16日 (四) 15:57 的最新修訂
內容大綱
簡介
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)與從中移除。(注意路徑不屬於圖片狀態。)
較複雜的範例
現在來一個複雜一點的範例。這範例用到路徑(path)與狀態(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()
的呼叫的結果會與 translate() translate() rotate()
一串的有所不同。
與 Apple <canvas> 的相容性
<canvas>
的實作大致上與 Apple 的相容。不過有些小地方需要注意一下。
要求的 </canvas>
標籤
<canvas>
在 Apple Safari 的實作裡與 <img>
相似,不需要結束標籤。但是要讓 <canvas>
在網路上能有更廣泛的用途,必須要有備用環境(fallback content)的機制。因此 Mozilla 的實作有要求結束標籤。
如果不需要備用環境,簡單的 <canvas id="foo" ...></canvas>
會與 Safari 和 Mozilla 完全相容 — Safari 會忽略那個結束標籤。
若是有備用環境,則必須要用到一些 CSS 技巧讓備用環境不會在 Safari 裡出現,但會出現在 Internet Explorer 裡。
其它的功能
把網頁內容畫到 Canvas 內
Mozilla 的 canvas
有用 drawWindow
延伸。這個函數把一個 DOM 視窗內容的快照的畫入 canvas 內。舉例來說
ctx.drawWindow(window, 0, 0, 100, 200, "rgb(0,0,0)");
會把目前視窗的內容中,相對於檢視區左上角的 rectangle (0,0,100,200) 的部份,用黑色背景畫到 canvas 內。顏色設定如用 "rgba(0,0,0,0)" 會使內容有透明背景(但會較慢)。
有了這個方法,我們可以做一個隱藏式的 iframe,放入任何內容,然後畫到 canvas 中。這圖片會照目前的轉換(transformation)來縮放與轉動等。
參見
- Canvas tutorial
- A Basic RayCaster
- Apple Canvas Documentation
- Rendering Web Page Thumbnails
- The Lightweight Visual Thesaurus
‧返回上一頁: 技術傳教專案主頁