diff options
Diffstat (limited to 'files/zh-tw/web/api/canvas_api/tutorial')
12 files changed, 3743 insertions, 0 deletions
diff --git a/files/zh-tw/web/api/canvas_api/tutorial/advanced_animations/index.html b/files/zh-tw/web/api/canvas_api/tutorial/advanced_animations/index.html new file mode 100644 index 0000000000..caacb185aa --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/advanced_animations/index.html @@ -0,0 +1,376 @@ +--- +title: Advanced animations +slug: Web/API/Canvas_API/Tutorial/Advanced_animations +translation_of: Web/API/Canvas_API/Tutorial/Advanced_animations +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}</div> + +<div class="summary"> +<p>在上一章節,我們做了一些<a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations">基礎動畫</a>且知道它的移動方式。在這部分我們更仔細的介紹它的動畫效果且並增加一些特效,使它看起來更高級。</p> +</div> + +<h2 id="畫一顆球">畫一顆球</h2> + +<p>在這次的動畫練習中使用球來練習。照著下面的步驟完成 canvas 設定。</p> + +<pre class="brush: html"><canvas id="canvas" width="600" height="300"></canvas> +</pre> + +<p>照常理,先在canvas上需要先畫一顆球。創造一個 <code>ball</code> object,它包含的屬性和<code>draw()</code>的方法,使canvas可以在上面繪圖。</p> + +<pre class="brush: js">var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); + +var ball = { + x: 100, + y: 100, + radius: 25, + color: 'blue', + draw: function() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.fill(); + } +}; + +ball.draw();</pre> + +<p>這裡沒什麼特別的,透過{{domxref("CanvasRenderingContext2D.arc()", "arc()")}}的方法,球事實上只是畫下簡單的圓。</p> + +<h2 id="添加速度">添加速度</h2> + +<p>現在有了一顆球,準備添加基礎的動畫像我們從<a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations">上章節學到的課程</a>。再次使用{{domxref("window.requestAnimationFrame()")}}控制動畫。添加移動的向量速度使球移動到向量點。對於每個幀(frame),我們使用{{domxref("CanvasRenderingContext2D.clearRect", "clear", "", 1)}}來清除canvas舊的移動幀(frame)。</p> + +<pre class="brush: js; highlight:[8,9,24,25]">var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +var raf; + +var ball = { + x: 100, + y: 100, + vx: 5, + vy: 2, + radius: 25, + color: 'blue', + draw: function() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.fill(); + } +}; + +function draw() { + ctx.clearRect(0,0, canvas.width, canvas.height); + ball.draw(); + ball.x += ball.vx; + ball.y += ball.vy; + raf = window.requestAnimationFrame(draw); +} + +canvas.addEventListener('mouseover', function(e) { + raf = window.requestAnimationFrame(draw); +}); + +canvas.addEventListener('mouseout', function(e) { + window.cancelAnimationFrame(raf); +}); + +ball.draw(); +</pre> + +<h2 id="邊界">邊界</h2> + +<p>沒有任何邊界碰撞下,球很快就會跑出canvas。這時需要確認球的 <code>x</code> and <code>y</code> 是否超出 canvas 尺寸,若超出則將球的向量顛倒。所以,我們添加了確認條件在<code>draw</code>方法:</p> + +<pre class="brush: js">if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) { + ball.vy = -ball.vy; +} +if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) { + ball.vx = -ball.vx; +}</pre> + +<h3 id="第一個示範">第一個示範</h3> + +<p>讓我們看看,看似很遠的行徑它如何行徑。移動你的滑鼠在canvas,使動畫開始。 </p> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas></pre> + +<pre class="brush: js">var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +var raf; + +var ball = { + x: 100, + y: 100, + vx: 5, + vy: 2, + radius: 25, + color: 'blue', + draw: function() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.fill(); + } +}; + +function draw() { + ctx.clearRect(0,0, canvas.width, canvas.height); + ball.draw(); + ball.x += ball.vx; + ball.y += ball.vy; + + if (ball.y + ball.vy > canvas.height || + ball.y + ball.vy < 0) { + ball.vy = -ball.vy; + } + if (ball.x + ball.vx > canvas.width || + ball.x + ball.vx < 0) { + ball.vx = -ball.vx; + } + + raf = window.requestAnimationFrame(draw); +} + +canvas.addEventListener('mouseover', function(e) { + raf = window.requestAnimationFrame(draw); +}); + +canvas.addEventListener('mouseout', function(e) { + window.cancelAnimationFrame(raf); +}); + +ball.draw();</pre> +</div> + +<p>{{EmbedLiveSample("First_demo", "610", "310")}}</p> + +<h2 id="加速性能">加速性能</h2> + +<p>為了使移動看起來更真實,你可以照著範例改變速度:</p> + +<pre class="brush: js">ball.vy *= .99; +ball.vy += .25;</pre> + +<p>這個使每個幀(frame)的垂直向量減少,所以球將只會在地板彈跳直到結束。</p> + +<div class="hidden"> +<h6 id="Second_demo">Second demo</h6> + +<pre class="brush: html"><canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas></pre> + +<pre class="brush: js">var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +var raf; + +var ball = { + x: 100, + y: 100, + vx: 5, + vy: 2, + radius: 25, + color: 'blue', + draw: function() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.fill(); + } +}; + +function draw() { + ctx.clearRect(0,0, canvas.width, canvas.height); + ball.draw(); + ball.x += ball.vx; + ball.y += ball.vy; + ball.vy *= .99; + ball.vy += .25; + + if (ball.y + ball.vy > canvas.height || + ball.y + ball.vy < 0) { + ball.vy = -ball.vy; + } + if (ball.x + ball.vx > canvas.width || + ball.x + ball.vx < 0) { + ball.vx = -ball.vx; + } + + raf = window.requestAnimationFrame(draw); +} + +canvas.addEventListener('mouseover', function(e) { + raf = window.requestAnimationFrame(draw); +}); + +canvas.addEventListener('mouseout', function(e) { + window.cancelAnimationFrame(raf); +}); + +ball.draw();</pre> +</div> + +<p>{{EmbedLiveSample("Second_demo", "610", "310")}}</p> + +<h2 id="追蹤效果">追蹤效果</h2> + +<p>直到現在我們已經使用{{domxref("CanvasRenderingContext2D.clearRect", "clearRect")}}方法清除之前的幀(frames)。如果使用重置半透明{{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}}這個方法,可以更淺顯的看出創造追蹤效果。</p> + +<pre class="brush: js">ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; +ctx.fillRect(0, 0, canvas.width, canvas.height);</pre> + +<div class="hidden"> +<h6 id="Third_demo">Third demo</h6> + +<pre class="brush: html"><canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas></pre> + +<pre class="brush: js">var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +var raf; + +var ball = { + x: 100, + y: 100, + vx: 5, + vy: 2, + radius: 25, + color: 'blue', + draw: function() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.fill(); + } +}; + +function draw() { + ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ball.draw(); + ball.x += ball.vx; + ball.y += ball.vy; + ball.vy *= .99; + ball.vy += .25; + + if (ball.y + ball.vy > canvas.height || + ball.y + ball.vy < 0) { + ball.vy = -ball.vy; + } + if (ball.x + ball.vx > canvas.width || + ball.x + ball.vx < 0) { + ball.vx = -ball.vx; + } + + raf = window.requestAnimationFrame(draw); +} + +canvas.addEventListener('mouseover', function(e) { + raf = window.requestAnimationFrame(draw); +}); + +canvas.addEventListener('mouseout', function(e) { + window.cancelAnimationFrame(raf); +}); + +ball.draw();</pre> +</div> + +<p>{{EmbedLiveSample("Third_demo", "610", "310")}}</p> + +<h2 id="增加滑鼠控制">增加滑鼠控制</h2> + +<p>為了能控制球使它跟著滑鼠移動,在這個範例使用<code><a href="/en-US/docs/Web/Reference/Events/mousemove">mousemove</a></code> 效果。當 <code><a href="/en-US/docs/Web/Events/click">click</a></code> 事件觸發了這顆球,它又會開始彈跳。</p> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas></pre> +</div> + +<pre class="brush: js">var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +var raf; +var running = false; + +var ball = { + x: 100, + y: 100, + vx: 5, + vy: 1, + radius: 25, + color: 'blue', + draw: function() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fillStyle = this.color; + ctx.fill(); + } +}; + +function clear() { + ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; + ctx.fillRect(0,0,canvas.width,canvas.height); +} + +function draw() { + clear(); + ball.draw(); + ball.x += ball.vx; + ball.y += ball.vy; + + if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) { + ball.vy = -ball.vy; + } + if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) { + ball.vx = -ball.vx; + } + + raf = window.requestAnimationFrame(draw); +} + +canvas.addEventListener('mousemove', function(e) { + if (!running) { + clear(); + ball.x = e.clientX; + ball.y = e.clientY; + ball.draw(); + } +}); + +canvas.addEventListener('click', function(e) { + if (!running) { + raf = window.requestAnimationFrame(draw); + running = true; + } +}); + +canvas.addEventListener('mouseout', function(e) { + window.cancelAnimationFrame(raf); + running = false; +}); + +ball.draw(); +</pre> + +<p>用你的滑鼠移動這顆球且點擊鬆放它。</p> + +<p>{{EmbedLiveSample("Adding_mouse_control", "610", "310")}}</p> + +<h2 id="突破性(遊戲)">突破性(遊戲)</h2> + +<p>這個小章節只有解釋一些創造高級動畫的技巧。這裡還有更多!如何增加槳,磚塊,到這個 到 <a href="http://en.wikipedia.org/wiki/Breakout_%28video_game%29">Breakout</a> game demo去看,有我們更<a href="/en-US/docs/Games">多遊戲研發</a>的文章! </p> + +<h2 id="延伸閱讀">延伸閱讀</h2> + +<ul> + <li>{{domxref("window.requestAnimationFrame()")}}</li> + <li><a href="/en-US/docs/Games/Techniques/Efficient_animation_for_web_games">Efficient animation for web games</a></li> +</ul> + +<p>{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html b/files/zh-tw/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html new file mode 100644 index 0000000000..86e41e3476 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html @@ -0,0 +1,669 @@ +--- +title: 套用樣式與顏色 +slug: Web/API/Canvas_API/Tutorial/Applying_styles_and_colors +translation_of: Web/API/Canvas_API/Tutorial/Applying_styles_and_colors +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}</div> + +<p>在<a href="/en-US/docs//zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes" title="/en-US/docs//en-US/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes">繪畫圖形</a>章節中,我們只用了預設的線條與填滿樣式,而在本章,我們將進一步看看所有可用的樣式選項,畫出更吸引人的圖。</p> + +<h2 id="Colors" name="Colors">顏色</h2> + +<p>U截至目前為止我們只有看到繪圖環境的方法(methods),如果我們想要設定圖形的顏色,我們有兩個屬性能用: <code>fillStyle</code>與<code>storkeStyle.</code></p> + +<dl> + <dt><code>fillStyle = <em>color</em></code></dt> + <dd>設定填滿圖形時用的顏色.</dd> + <dt><code>strokeStyle = <em>color</em></code></dt> + <dd>設定勾勒圖形時用的顏色.</dd> +</dl> + +<p>其中<code>color</code>可以是CSS{{cssxref("<color>")}}表示字串、漸層色物件(gradient color)或是模式物件(pattern object),現在先看一下CSS{<color>}表示字串,稍後再看另外兩個項目.</p> + +<p>預設上勾勒和填滿色是黑色(CSS顏色值為#000000).</p> + +<div class="note"> +<p><strong>Note:</strong> 一旦改變了strokeStyle的顏色值,那麼之後圖形勾勒顏色都會變成新顏色,同樣狀況一樣適用於fillStyle.</p> +</div> + +<p>合格的顏色值請參照CSS3{{cssxref("<color>")}}規範,下面範例所標示的顏色都指向同一個顏色.</p> + +<pre class="brush: js">// these all set the fillStyle to 'orange' + +ctx.fillStyle = "orange"; +ctx.fillStyle = "#FFA500"; +ctx.fillStyle = "rgb(255,165,0)"; +ctx.fillStyle = "rgba(255,165,0,1)"; +</pre> + +<div class="note"> +<p><strong>Note:</strong> 目前Gecko引擎並不支援CSS3全部的顏色值,例如hsl(100%,25%,0)和rgb(0,100%,0)就不被支援.</p> +</div> + +<h3 id="A_fillStyle_example" name="A_fillStyle_example"><code>fillStyle</code>範例</h3> + +<p>這裡我們利用兩個for迴圈來畫出一個矩形陣列,而且陣列中每一個矩形的顏色都不相同。下面程式碼透過改變i和j兩個變數來分別變換RGB中的紅色值和綠色值,然後為每一個矩形產生自己專屬的顏色值。透過改變RGB的各顏色值,我們可以產生各式各樣的調色盤,像是逐步調整顏色值,你也可以做出像Photoshop內建一樣的調色盤。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + for (var i=0;i<6;i++){ + for (var j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } +}</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>結果如下:</p> + +<p>{{EmbedLiveSample("A_fillStyle_example", 160, 160, "https://mdn.mozillademos.org/files/5417/Canvas_fillstyle.png")}}</p> + +<h3 id="A_strokeStyle_example" name="A_strokeStyle_example"><code>strokeStyle</code>範例</h3> + +<p>本例和前例相當類似,不同的是我們改用arc()方法畫圓形而不是矩形、改設定strokeStyle變換圖形輪廓顏色。</p> + +<pre class="brush: js"> function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + for (var i=0;i<6;i++){ + for (var j=0;j<6;j++){ + ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ')'; + ctx.beginPath(); + ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true); + ctx.stroke(); + } + } + } +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>結果如下:</p> + +<p>{{EmbedLiveSample("A_strokeStyle_example", "180", "180", "https://mdn.mozillademos.org/files/253/Canvas_strokestyle.png")}}</p> + +<h2 id="Transparency" name="Transparency">透明度</h2> + +<p>透過設定globalAlpha屬性或是以半透明顏色值設定strokeStyle與fillStyle屬性,除了畫不透明的圖形,我們還可以畫半透明的圖形。</p> + +<dl> + <dt><code>globalAlpha = <em>transparencyValue</em></code></dt> + <dd>允許值介於0.0(全透明)到1.0(不透明)。一旦設定後,之後畫布上畫的所有圖形的不透明度都會套用此設定值。預設值為1.0。</dd> +</dl> + +<p>當我們想畫一系列相同不透明度的圖,設定globalAlpha值是一個方便的作法。</p> + +<p>由CSS3顏色值能夠指定不透明度,我們也可以如下面一般,設定strokeStyle以及fillStyle來變更不透明度。</p> + +<pre class="brush: js">// Assigning transparent colors to stroke and fill style + +ctx.strokeStyle = "rgba(255,0,0,0.5)"; +ctx.fillStyle = "rgba(255,0,0,0.5)"; +</pre> + +<p>rgba()函數比rgb()函數多出一個不透明度參數,允許值介於0.0(全透明)到1.0(不透明).</p> + +<h3 id="A_globalAlpha_example" name="A_globalAlpha_example"><code>globalAlpha</code>範例</h3> + +<p>下面我們將在四個方格色塊背景上畫一系列半透明圓形。對於所有圓形,我們藉由設置globalAlpha屬性值為0.2使得圓形變成半透明,然後for迴圈裡我們逐一增加圓形繪圖半徑,最終結果看起來便像是輻射狀漸層圖案,而且圓形相互疊加在彼此之上後,又加深了重疊區域的不透明度,只要我們不斷增加圓形數量,最後圖片中央將被完全遮蓋,看不到背後的背景。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + // draw background + ctx.fillStyle = '#FD0'; + ctx.fillRect(0,0,75,75); + ctx.fillStyle = '#6C0'; + ctx.fillRect(75,0,75,75); + ctx.fillStyle = '#09F'; + ctx.fillRect(0,75,75,75); + ctx.fillStyle = '#F30'; + ctx.fillRect(75,75,150,150); + ctx.fillStyle = '#FFF'; + + // set transparency value + ctx.globalAlpha = 0.2; + + // Draw semi transparent circles + for (i=0;i<7;i++){ + ctx.beginPath(); + ctx.arc(75,75,10+10*i,0,Math.PI*2,true); + ctx.fill(); + } +}</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_globalAlpha_example", "180", "180", "https://mdn.mozillademos.org/files/232/Canvas_globalalpha.png")}}</p> + +<h3 id="An_example_using_rgba()" name="An_example_using_rgba()"><code>rgba()</code>使用範例</h3> + +<p>這個範例類似於上面的範例,但不同的是我們改畫半透明的矩形。rgba()在使用上會多一點彈性,因為我們可以分別設置勾勒和填滿圖形的不透明度。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + // Draw background + ctx.fillStyle = 'rgb(255,221,0)'; + ctx.fillRect(0,0,150,37.5); + ctx.fillStyle = 'rgb(102,204,0)'; + ctx.fillRect(0,37.5,150,37.5); + ctx.fillStyle = 'rgb(0,153,255)'; + ctx.fillRect(0,75,150,37.5); + ctx.fillStyle = 'rgb(255,51,0)'; + ctx.fillRect(0,112.5,150,37.5); + + // Draw semi transparent rectangles + for (var i=0;i<10;i++){ + ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')'; + for (var j=0;j<4;j++){ + ctx.fillRect(5+i*14,5+j*37.5,14,27.5) + } + } +}</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("An_example_using_rgba()", "180", "180", "https://mdn.mozillademos.org/files/246/Canvas_rgba.png")}}</p> + +<h2 id="Line_styles" name="Line_styles">線條樣式</h2> + +<p>有數種屬性可以讓我們設定線條樣式.</p> + +<dl> + <dt><code>lineWidth = <em>value</em></code></dt> + <dd>設定線條寬度。</dd> + <dt><code>lineCap = <em>type</em></code></dt> + <dd>設定線條結尾的樣式。</dd> + <dt><code>lineJoin = <em>type</em></code></dt> + <dd>設定線條和線條間接合處的樣式。</dd> + <dt><code>miterLimit = <em>value</em></code></dt> + <dd>限制當兩條線相交時交接處最大長度;所謂交接處長度(miter length)是指線條交接處內角頂點到外角頂點的長度。</dd> +</dl> + +<p>底下我們將一一示範這些屬性的用途。</p> + +<h3 id="A_lineWidth_example" name="A_lineWidth_example"><code>lineWidth</code>範例</h3> + +<p>此屬性決定線條寬度,必須為正數,預設值為1.0單位。</p> + +<p>線條寬度的起算點是從繪圖路徑中央開始往兩旁各延伸一半設定寬度,由於畫布座標不直接對應到像素(pixel),所以要比較小心設定好取得清晰的直線。</p> + +<p>由下方例子可以明顯看到,畫布上有10條直線,由左至右,從最小的1.0單位寬開始逐漸加寬,請注意奇數寬度直線會因為繪圖路徑位置關係而比較模糊。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + for (var i = 0; i < 10; i++){ + ctx.lineWidth = 1+i; + ctx.beginPath(); + ctx.moveTo(5+i*14,5); + ctx.lineTo(5+i*14,140); + ctx.stroke(); + } +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_lineWidth_example", "180", "180", "https://mdn.mozillademos.org/files/239/Canvas_linewidth.png")}}</p> + +<p>為了畫出清晰的直線,我們需要了解繪圖路徑是如何產生;如下方圖示,網格代表畫布座標軸,網格所框出的方格則代表螢幕上的像素,第一張圖片填滿了座標(2,1)到(5,5)的紅色區域,而這個紅色區域的邊際正好符合像素間的邊際,所以會產生出清晰的影像。</p> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/201/Canvas-grid.png"></p> + +<p>第二張圖片中,有一條寬1.0單位的直線從座標(3,1)到(3,5)被畫在畫布上,不過由於線條寬度的起算點是從繪圖路徑中央開始往兩旁各延伸一半設定寬度,所以當勾勒線條時,繪圖路徑兩旁的像素格只有一半會被填滿暗藍色,至於另外一半則會經由計算填入近似色(淡藍色),結果就是整格像素並非全部填入相同的暗藍色,進而產生出邊緣較為模糊的線條,上面程式碼範例中的奇數寬度直線就是因此而產生不清晰的線條。</p> + +<p>為了避免劃出邊緣模糊直線,我們必須精準設定繪圖路徑位置,就本範例而言,如果我們的直線繪圖路徑是從座標(3.5, 1)到(3.5, 5)的話(如第三張圖),那麼1.0單位寬的直線將剛好填滿像素格,所以我們將可以畫出清晰的直線。</p> + +<div class="note"> +<p><strong>Note:</strong> 請注意本範例的Y軸座標都是整數點,若非如此,一樣會導致線條端點的像素格無法剛好被填滿的現象,而且同時最後產生的結果也會被lineCap給影響;倘若lineCap值為預設butt時,我們會需要為奇數寬度直線計算一下非整數的座標點,倘若lineCap樣式為square,那麼線段端點的像素格將自動被完整填滿。</p> + +<p>還有一點需要注意,只要繪圖路徑被closePath()函數閉合起來,這樣便沒有了線條端點,所有的線條端點都會依據lineJoin樣式全部前後互相連接起來,這會自動延伸端點邊緣到線段接合處,如果此時接合端點是水平或垂直的話,位於中央的像素格將會被完整填滿。後面的說明會介紹lineCap和lineJoin樣式。</p> +</div> + +<p>至於本例中偶數寬度的直線,為了避免模糊,繪圖路徑最好是落在整數座標點上。</p> + +<p>雖然處裡2D繪圖縮放有些麻煩,但只要仔細計算像素格和繪圖路徑位置,縱使進行圖像縮放或變形,圖像輸出還是可以保持正確。一條寬1.0單位的直線,只要位置計算正確,放大兩倍後會變成一條2個像素寬的清晰直線,而且還是會保持正確位置。</p> + +<h3 id="A_lineCap_example" name="A_lineCap_example"><code>lineCap</code>範例</h3> + +<p>這個屬性決定線條端點的樣式,總共有三種樣式可選:</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/236/Canvas_linecap.png" style="float: right; height: 190px; width: 190px;"></p> + +<dl> + <dt><code>butt</code></dt> + <dd>線條端點樣式為方形</dd> + <dt><code>round</code></dt> + <dd>線條端點樣式為圓形</dd> + <dt><code>square</code></dt> + <dd>增加寬同線條寬度、高線條寬度一半的的方塊於線條端點</dd> +</dl> + +<p>下面程式碼會畫出三條線,每條線的lineCap值皆不同。然後為了看清差異點,我們加上了兩條淡藍色的輔助線,線條的繪圖起始點和終點都剛好落在輔助線上。</p> + +<p>最左邊的線條其lineCap為butt,不難看出它完全介於輔助線之間;第二條線其lineCap為round,端點樣式為半徑等於線條寬度一半的半圓;最右邊的線條其lineCap為square,端點樣式為寬同線條寬度、高線條寬度一半的的方塊。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + var lineCap = ['butt','round','square']; + + // Draw guides + ctx.strokeStyle = '#09f'; + ctx.beginPath(); + ctx.moveTo(10,10); + ctx.lineTo(140,10); + ctx.moveTo(10,140); + ctx.lineTo(140,140); + ctx.stroke(); + + // Draw lines + ctx.strokeStyle = 'black'; + for (var i=0;i<lineCap.length;i++){ + ctx.lineWidth = 15; + ctx.lineCap = lineCap[i]; + ctx.beginPath(); + ctx.moveTo(25+i*50,10); + ctx.lineTo(25+i*50,140); + ctx.stroke(); + } +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_lineCap_example", "180", "180", "https://mdn.mozillademos.org/files/236/Canvas_linecap.png")}}</p> + +<h3 id="A_lineJoin_example" name="A_lineJoin_example"><code>lineJoin</code>範例</h3> + +<p>lineJoin屬性決定兩個連接區端(如線條、弧形或曲線)如何連接(對於長度為零,亦即終點和控制點為同一點的圖形無效)。</p> + +<p>lineJoin屬性共有三個屬性值如下,其中miter為預設值,請注意一點若是兩個連接區段的繪圖方向一致,那代表不會有連接處,所以測定是無效的。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/237/Canvas_linejoin.png" style="float: right; height: 190px; width: 190px;"></p> + +<dl> + <dt><code>round</code></dt> + <dd>代表圓弧型連接樣式。</dd> + <dt><code>bevel</code></dt> + <dd>代表斜面型連接樣式。在連接區段的共同終點處填滿一個三角形區域,將原本的外接角處形成一個切面。</dd> + <dt><code>miter</code></dt> + <dd>代表斜交型連接樣式。向外延伸連結區段外緣直到相交於一點,然後形成菱形區域,而miterLimit屬性會影響miter屬性。</dd> +</dl> + +<p>下方程式碼和圖形輸出展示了lineJoin在不同屬性值下呈現的不同結果</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + var lineJoin = ['round','bevel','miter']; + ctx.lineWidth = 10; + for (var i=0;i<lineJoin.length;i++){ + ctx.lineJoin = lineJoin[i]; + ctx.beginPath(); + ctx.moveTo(-5,5+i*40); + ctx.lineTo(35,45+i*40); + ctx.lineTo(75,5+i*40); + ctx.lineTo(115,45+i*40); + ctx.lineTo(155,5+i*40); + ctx.stroke(); + } +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_lineJoin_example", "180", "180", "https://mdn.mozillademos.org/files/237/Canvas_linejoin.png")}}</p> + +<h3 id="A_demo_of_the_miterLimit_property" name="A_demo_of_the_miterLimit_property"><code>miterLimit</code>屬性</h3> + +<p>前面範例顯示出,當lineJoin值為miter時,兩條線的外緣會延伸相交,所以,當這兩條相交線的相交角度越小的話,他們的延伸交會點就會越遠離內緣連接點,而且隨著角度變小,距離呈指數型增長。</p> + +<p>miterLimit會限制延伸交會點最遠可以離內緣連接點到多遠,當延伸交會點的落點超出這個範圍,那麼便以斜面(bevel)作為交接樣式。請注意,最大miter長度為線寬乘於miterLimit值,所以miterLimit可以獨立於目前顯示縮放尺寸或其他變形設定。</p> + +<p>miterLimit預設值為10.0。</p> + +<p>更精確來說,miter限制是指延伸長度(在HTML畫布上,這個長度是外緣相交角到連接區段的共同繪圖路經終點)相對於一半線寬的最大允許比率;也等同於,外緣距內緣相交點之距離相對於線寬的的最大允許比率;相當於,連接區最小內緣角的一半角度的餘割(cosecant)值, 小於此值則便以斜面(bevel)作為交接樣式:</p> + +<ul> + <li><code>miterLimit</code> = <strong>max</strong> <code>miterLength</code> / <code>lineWidth</code> = 1 / <strong>sin</strong> ( <strong>min</strong> <em>θ</em> / 2 )</li> + <li>10.0的預設miterLimit值會移除任何角度小於11度的相接線段的miter交接。</li> + <li>miter限制值如果等於根號2(約1.4142136)會移除銳角的miter交接,只有直角或鈍角的不會被移除。</li> + <li>miter限制值如果等於1.0會移除所有的miter交接。</li> + <li>小於1.0不是合法的限制值。</li> +</ul> + +<p>下面是一個範例,其中藍線標示出各個線條繪圖路徑的起始點與終點。</p> + +<p>倘若設定範例程式碼中的miterLimit低於4.2,所有的miter交接都會被移除,取而代之的是出現在藍線附近的bevel交接;倘若設定miterLimit大於10,那麼大部分的miter交接都會出現,而且你會發現,由左到右,miter長度逐漸縮短,這是由於線條相交角度逐漸加大之故;倘若設定中間值,那麼左邊會出現bevel交接,右邊會出現miter交接。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + // Clear canvas + ctx.clearRect(0,0,150,150); + + // Draw guides + ctx.strokeStyle = '#09f'; + ctx.lineWidth = 2; + ctx.strokeRect(-5,50,160,50); + + // Set line styles + ctx.strokeStyle = '#000'; + ctx.lineWidth = 10; + + // check input + if (document.getElementById('miterLimit').value.match(/\d+(\.\d+)?/)) { + ctx.miterLimit = parseFloat(document.getElementById('miterLimit').value); + } else { + alert('Value must be a positive number'); + } + + // Draw lines + ctx.beginPath(); + ctx.moveTo(0,100); + for (i=0;i<24;i++){ + var dy = i%2==0 ? 25 : -25 ; + ctx.lineTo(Math.pow(i,1.5)*2,75+dy); + } + ctx.stroke(); + return false; +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><table> + <tr> + <td><canvas id="canvas" width="150" height="150"></canvas></td> + <td>Change the <code>miterLimit</code> by entering a new value below and clicking the redraw button.<br><br> + <form onsubmit="return draw();"> + <label>Miter limit</label> + <input type="text" size="3" id="miterLimit"/> + <input type="submit" value="Redraw"/> + </form> + </td> + </tr> +</table></pre> + +<pre class="brush: js">document.getElementById('miterLimit').value = document.getElementById('canvas').getContext('2d').miterLimit; +draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_demo_of_the_miterLimit_property", "400", "180", "https://mdn.mozillademos.org/files/240/Canvas_miterlimit.png")}}</p> + +<h2 id="Gradients" name="Gradients">漸層</h2> + +<p>如同其他繪圖軟體可以畫出線性和放射狀的漸層圖案,透過設定fillStyle和strokeStyle屬性為canvasGradient漸層物件,我們也可以在canvas上做到一樣的效果。要創造漸層物件,可以使用下面的方法:</p> + +<dl> + <dt><code>createLinearGradient(<em>x1</em>, <em>y1</em>, <em>x2</em>, <em>y2</em>)</code></dt> + <dd>產生一個線性漸層物件,其漸層起始點為(x1, y1)、終點為(x2, y2)。</dd> + <dt><code>createRadialGradient(<em>x1</em>, <em>y1</em>, <em>r1</em>, <em>x2</em>, <em>y2</em>, <em>r2</em>)</code></dt> + <dd>產生一個放射狀漸層物件,第一個圓之圓心落在(x1, y1)、半徑為r1,第一個圓之圓心落在(x2, y2)、半徑為r2。</dd> +</dl> + +<p>例如:</p> + +<pre class="brush: js">var lineargradient = ctx.createLinearGradient(0, 0, 150, 150); +var radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100); +</pre> + +<p>一旦產生了canvasGradient漸層物件,我們用addColorStop()方法可以添加顏色上去。</p> + +<dl> + <dt><code><em>gradient</em>.addColorStop(<em>position</em>, <em>color</em>)</code></dt> + <dd>於gradient漸層物件建立一個顏色點,其中color是CSS{{cssxref("<color>")}}的字串表示,而position介於0.0到1.0之間,定義了該顏色在漸層中的相對位置。呼叫這個方法會指定當進行到設定的位置時,漸層需要完全轉變成設定的顏色。</dd> +</dl> + +<p>我們可以按照需要設定無數個顏色點,下面是一個簡單的由白到黑的簡單漸層範例程式碼。</p> + +<pre class="brush: js">var lineargradient = ctx.createLinearGradient(0,0,150,150); +lineargradient.addColorStop(0, 'white'); +lineargradient.addColorStop(1, 'black'); +</pre> + +<h3 id="A_createLinearGradient_example" name="A_createLinearGradient_example"><code>createLinearGradient</code>範例</h3> + +<p>本範例中,我們將建立兩種漸層,如範例所示,strokeStyle和fillSyle屬性都可以接受canvasGradient物件作為屬性值。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + // Create gradients + var lingrad = ctx.createLinearGradient(0,0,0,150); + lingrad.addColorStop(0, '#00ABEB'); + lingrad.addColorStop(0.5, '#fff'); + lingrad.addColorStop(0.5, '#26C000'); + lingrad.addColorStop(1, '#fff'); + + var lingrad2 = ctx.createLinearGradient(0,50,0,95); + lingrad2.addColorStop(0.5, '#000'); + lingrad2.addColorStop(1, 'rgba(0,0,0,0)'); + + // assign gradients to fill and stroke styles + ctx.fillStyle = lingrad; + ctx.strokeStyle = lingrad2; + + // draw shapes + ctx.fillRect(10,10,130,130); + ctx.strokeRect(50,50,50,50); + +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>第一個漸層為背景漸層,範例中我們在一個位置上指定了兩種顏色(白色到綠色),這樣做會產生非常突然的顏色轉換,一般來說,不管如何設定顏色點順序都沒關係,然而就這個例子而言,這種作法太過強烈了,但是如果這是你想要的顏色漸層順序,那其實也是可以。</p> + +<p>第二個漸層起始位置(position 0.0)的顏色並沒有被指定,所以下一個漸層顏色會自動被設為起始位置顏色,因此即使我們沒有指定漸層起始位置顏色也沒有關係,就像本範例自動會設定起始位置的顏色等於位置0.5的黑色。</p> + +<p>{{EmbedLiveSample("A_createLinearGradient_example", "180", "180", "https://mdn.mozillademos.org/files/235/Canvas_lineargradient.png")}}</p> + +<h3 id="A_createRadialGradient_example" name="A_createRadialGradient_example"><code>createRadialGradient</code>範例</h3> + +<p>這邊我們定義了四種放射狀漸層,相較於一般在Photoshop看到的”經典”放射狀漸層圖案(漸層從一個圖案中心點向外呈圓心狀延伸),因為我們可以控制漸層起始和終止點,我們可以做到更好的效果。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + // Create gradients + var radgrad = ctx.createRadialGradient(45,45,10,52,50,30); + radgrad.addColorStop(0, '#A7D30C'); + radgrad.addColorStop(0.9, '#019F62'); + radgrad.addColorStop(1, 'rgba(1,159,98,0)'); + + var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50); + radgrad2.addColorStop(0, '#FF5F98'); + radgrad2.addColorStop(0.75, '#FF0188'); + radgrad2.addColorStop(1, 'rgba(255,1,136,0)'); + + var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40); + radgrad3.addColorStop(0, '#00C9FF'); + radgrad3.addColorStop(0.8, '#00B5E2'); + radgrad3.addColorStop(1, 'rgba(0,201,255,0)'); + + var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90); + radgrad4.addColorStop(0, '#F4F201'); + radgrad4.addColorStop(0.8, '#E4C700'); + radgrad4.addColorStop(1, 'rgba(228,199,0,0)'); + + // draw shapes + ctx.fillStyle = radgrad4; + ctx.fillRect(0,0,150,150); + ctx.fillStyle = radgrad3; + ctx.fillRect(0,0,150,150); + ctx.fillStyle = radgrad2; + ctx.fillRect(0,0,150,150); + ctx.fillStyle = radgrad; + ctx.fillRect(0,0,150,150); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>程式碼範例中,為了營造出3D效果,我們讓起始點和終止點位於不同位置,請注意,最好不要讓內外圈相重疊,以避免難以預測的奇怪效果。</p> + +<p>每一個漸層圖案最後一個漸層色都是全透明的,如果希望倒數第二個漸層色能夠平順地轉換到這個最後一個漸層色,那麼兩者應該設定一樣的顏色值,像是程式碼範例中的漸層色 #019F62 其實就等於 rgba(1,159,98,1)。</p> + +<p>{{EmbedLiveSample("A_createRadialGradient_example", "180", "180", "https://mdn.mozillademos.org/files/244/Canvas_radialgradient.png")}}</p> + +<h2 id="Patterns" name="Patterns">樣式(Patterns)</h2> + +<p>先前的範例中,我們都是藉由迴圈來重複產生影像樣式,不過其實有一條更簡單的方法,那就是呼叫createPattern方法。</p> + +<dl> + <dt><code>createPattern(<em>image</em>, <em>type</em>)</code></dt> + <dd>呼叫createPattern()會產一個畫布樣式物件,然後回傳出來。<br> + 其中image是<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#image-sources-for-2d-rendering-contexts" title="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#image-sources-for-2d-rendering-contexts">CanvasImageSource</a>類別物件(像是{{domxref("HTMLImageElement")}},、<canvas>元素、{{HTMLElement("video")}} 元素等)</dd> +</dl> + +<p>Type是一串字串,定義了如何產生樣式,允許的值有:</p> + +<dl> + <dt><code>repeat</code></dt> + <dd>沿垂直與水平方向重複排列影像</dd> + <dt><code>repeat-x</code></dt> + <dd>只沿水平方向重複排列影像</dd> + <dt><code>repeat-y</code></dt> + <dd>只沿垂直方向重複排列影像</dd> + <dt><code>no-repeat</code></dt> + <dd>不重複排列影像,只使用一次</dd> +</dl> + +<div class="note"> +<p><strong>Note:</strong> Firefox現在只支援repeat,所以其他值都是無效的</p> +</div> + +<div class="note"><strong>Note:</strong> 傳入尺寸為0x0像素的畫布會引起錯誤</div> + +<p>利用createPattern()的方法和前面利用漸層的方法十分類似,我們呼叫createPattern()產生{{domxref("CanvasPattern")}}物件,然後將{CanvasPattern}物件設成fillStyle或strokeStyle的屬性值,例如:</p> + +<pre class="brush: js">var img = new Image(); +img.src = 'someimage.png'; +var ptrn = ctx.createPattern(img,'repeat'); +</pre> + +<div class="note"> +<p><strong>Note:</strong> 不像drawImage()方法,呼叫createPattern()方法前影像必須要先載入完成,否則可能圖像的程生會有問題。</p> +</div> + +<h3 id="A_createPattern_example" name="A_createPattern_example"><code>createPattern</code>範例</h3> + +<p>這個範例中我們把fillStyle屬性值存為樣式物件,比較值得注意的是影像onload事件處理器,這是為了確保影像載入完成後再進行。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + // create new image object to use as pattern + var img = new Image(); + img.src = '/files/222/Canvas_createpattern.png'; + img.onload = function(){ + + // create pattern + var ptrn = ctx.createPattern(img,'repeat'); + ctx.fillStyle = ptrn; + ctx.fillRect(0,0,150,150); + + } +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> + +<p>結果如下<span style="display: none;"> </span>:</p> +</div> + +<p>{{EmbedLiveSample("A_createPattern_example", "180", "180", "https://mdn.mozillademos.org/files/222/Canvas_createpattern.png")}}</p> + +<h2 id="陰影">陰影</h2> + +<p>要產生陰影只需要四個屬性:</p> + +<dl> + <dt><code>shadowOffsetX = <em>float</em></code></dt> + <dd>代表陰影從物件延伸出來的水平距離,預設為0,不受變形矩陣影響。</dd> + <dt><code>shadowOffsetY = <em>float</em></code></dt> + <dd>代表陰影從物件延伸出來的垂直距離,預設為0,不受變形矩陣影響。</dd> + <dt><code>shadowBlur = <em>float</em></code></dt> + <dd>代表陰影模糊大小範圍,預設為0,不受變形矩陣影響,不等同於像素值。</dd> + <dt><code>shadowColor = {{cssxref("<color>")}}</code></dt> + <dd>CSS顏色值,代表陰影顏色,預設為全透明。</dd> +</dl> + +<p><code>shadowOffsetX和shadowOffsetY會決定陰影延伸大小,若是為正值,則陰影會往右(沿X軸)和往下(沿Y軸)延伸,若是為負值,則會往正值相反方向延伸。</code></p> + +<div class="geckoVersionNote"> +<p>{{gecko_callout_heading("7.0")}}</p> + +<p>Note: 基於HTML5提議規格變更,從{{Gecko("7.0")}}開始,陰影只會在source-over的<a href="/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing" title="/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing">構圖排列</a>下產生</p> +</div> + +<h3 id="文字陰影範例">文字陰影範例</h3> + +<p>本程式碼範例會產生一串帶有陰影的文字。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 2; + ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; + + ctx.font = "20px Times New Roman"; + ctx.fillStyle = "Black"; + ctx.fillText("Sample String", 5, 30); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="80"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_shadowed_text_example", "180", "100", "https://mdn.mozillademos.org/files/2505/shadowed-string.png")}}</p> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Using_images", "Web/Guide/HTML/Canvas_tutorial/Transformations")}}</p> + +<div id="cke_pastebin" style="position: absolute; top: 12776.2px; width: 1px; height: 1px; overflow: hidden; left: -1000px;"> </div> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/basic_animations/index.html b/files/zh-tw/web/api/canvas_api/tutorial/basic_animations/index.html new file mode 100644 index 0000000000..336fadbce0 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/basic_animations/index.html @@ -0,0 +1,347 @@ +--- +title: 基礎動畫 +slug: Web/API/Canvas_API/Tutorial/Basic_animations +translation_of: Web/API/Canvas_API/Tutorial/Basic_animations +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}</div> + +<p>控制{{HTMLElement("canvas")}}元素來產生互動式動畫不是一件難事,當然,如果產生的動畫越複雜越需要多費一些力氣,未來如果有機會我們將說明這一塊。</p> + +<p>由於圖形一但產生後便靜止不動,所以我們必須重新繪圖好移動圖案,產生動畫效果,所以如果繪圖越複雜,繪圖運算也需要消耗越多運算資源和時間,換句話說,電腦效能的好壞將大大影響動畫順暢度,或許這也是畫布動畫最大的限制。</p> + +<h2 id="Basic_animation_steps" name="Basic_animation_steps">動畫基本步驟</h2> + +<p>產生一個畫面基本上需要以下步驟 :</p> + +<ol> + <li><strong>清除畫布</strong><br> + 除了不變的背景畫面,所有先前畫的圖案都要先清除,這個步驟可以透過clearRect()方法達成。</li> + <li><strong>儲存畫布狀態</strong><br> + 若是想要每一次重新繪圖時畫布起始狀態都是原始狀態,那麼就需要先行儲存畫布原始狀態。</li> + <li><strong>畫出畫面</strong><br> + 畫出需要畫面。</li> + <li><strong>復原畫布狀態</strong><br> + 復原畫布狀態以備下次繪圖使用。</li> +</ol> + +<h2 id="Controlling_an_animation" name="Controlling_an_animation">控制動畫</h2> + +<p>一般來說當程式碼執行完畢後我們才會看到繪圖結果,所以說我們無法靠執行for迴圈來產生動畫,我們得靠每隔一段時間繪圖來產生動畫,下面將介紹兩種作法。</p> + +<h3 id="排程更新">排程更新</h3> + +<p>第一種作法是利用{{domxref("window.setInterval()")}}與{{domxref("window.setTimeout()")}}方法。</p> + +<div class="note"> +<p><strong>Note:</strong> 針對新版瀏覽器建議採用{{domxref("window.requestAnimationFrame()")}}方法。</p> +</div> + +<dl> + <dt><code>setInterval(<em>function</em>, <em>delay</em>)</code></dt> + <dd>每隔delay毫秒,執行輸入function(函數)</dd> + <dt><code>setTimeout(<em>function</em>, <em>delay</em>)</code></dt> + <dd>過delay毫秒後,執行輸入function(函數)</dd> + <dt>requestAnimationFrame(callback)</dt> + <dd>告訴瀏覽器你希望執行動畫的時候,要求瀏覽器在重繪下一張畫面之前,呼叫callback函數來更新動畫</dd> +</dl> + +<p>如果希望不要有任何的使用者互動影響,請使用setInterval(),因為它會確實地每隔一段時間就執行程式碼。如果你想製作遊戲 , 我們能夠使用keyboard 或是 mouse event來控制動畫,並使用setTimeout()函數一起。藉由設定EventListeners,我們能夠捕捉任何使用者的動作,並執行我們的動畫函數。</p> + +<div class="note"> +<p>在下面的範例,我們將使用<strong>window.requestAnimationFrame()</strong>方法來控制動畫,<strong>window.requestAnimationFrame()</strong>方法為動畫提供更順暢更有效率的方式來執行,當系統準備好繪製畫面時,藉由呼叫動畫andmation frame()的callback函數 。callback通常每秒鐘執行60次,當執行background tab時,執行次數會更低,想知道更多關於動畫迴圈(animation loop)的資訊,尤其是遊戲的應用,請查看我們在 <a href="https://developer.mozilla.org/en-US/docs/Games">Game development zone</a> 的主題 <a href="https://developer.mozilla.org/en-US/docs/Games/Anatomy">Anatomy of a video game</a> 。</p> +</div> + +<h3 id="從使用者輸入操作控制動畫">從使用者輸入操作控制動畫</h3> + +<p>我們也可以從使用者輸入操作控制動畫,就像是電玩遊戲一般;像是在鍵盤上設置事件處理器{{domxref("EventListener")}}捕捉使用者輸入並執行對應動畫。</p> + +<p>你可以利用我們的<a href="https://developer.mozilla.org/en-US/docs/DOM/window.setInterval#A_little_framework" title="https://developer.mozilla.org/en-US/docs/DOM/window.setInterval#A_little_framework">次要版</a>或<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Timers/Daemons" title="https://developer.mozilla.org/en-US/docs/JavaScript/Timers/Daemons">主要版動畫框架</a>。</p> + +<pre>var myAnimation = new MiniDaemon(null, animateShape, 500, Infinity);</pre> + +<p>或</p> + +<pre>var myAnimation = new Daemon(null, animateShape, 500, Infinity);</pre> + +<p>在後面的範例我們主要將使用window.setInterval()方法控制動畫,然後於本頁底部是一些使用widnow.setTimeout()的範例連結。</p> + +<h4 id="太陽系動畫">太陽系動畫</h4> + +<p>本例會產生一個小型太陽系運行動畫。</p> + +<pre class="brush: js">var sun = new Image(); +var moon = new Image(); +var earth = new Image(); +function init(){ + sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png'; + moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png'; + earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png'; + setInterval(draw,100); +} + +function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + ctx.globalCompositeOperation = 'destination-over'; + ctx.clearRect(0,0,300,300); // clear canvas + + ctx.fillStyle = 'rgba(0,0,0,0.4)'; + ctx.strokeStyle = 'rgba(0,153,255,0.4)'; + ctx.save(); + ctx.translate(150,150); + + // Earth + var time = new Date(); + ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() ); + ctx.translate(105,0); + ctx.fillRect(0,-12,50,24); // Shadow + ctx.drawImage(earth,-12,-12); + + // Moon + ctx.save(); + ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() ); + ctx.translate(0,28.5); + ctx.drawImage(moon,-3.5,-3.5); + ctx.restore(); + + ctx.restore(); + + ctx.beginPath(); + ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit + ctx.stroke(); + + ctx.drawImage(sun,0,0,300,300); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="300" height="300"></canvas></pre> + +<pre class="brush: js">init();</pre> +</div> + +<p>{{EmbedLiveSample("An_animated_solar_system", "310", "310", "https://mdn.mozillademos.org/files/202/Canvas_animation1.png")}}</p> + +<h4 id="時鐘動畫">時鐘動畫</h4> + +<p>本例會產生一個時鐘指向現在時間。</p> + +<pre class="brush: js">function init(){ + clock(); + setInterval(clock,1000); +} + +function clock(){ + var now = new Date(); + var ctx = document.getElementById('canvas').getContext('2d'); + ctx.save(); + ctx.clearRect(0,0,150,150); + ctx.translate(75,75); + ctx.scale(0.4,0.4); + ctx.rotate(-Math.PI/2); + ctx.strokeStyle = "black"; + ctx.fillStyle = "white"; + ctx.lineWidth = 8; + ctx.lineCap = "round"; + + // Hour marks + ctx.save(); + for (var i=0;i<12;i++){ + ctx.beginPath(); + ctx.rotate(Math.PI/6); + ctx.moveTo(100,0); + ctx.lineTo(120,0); + ctx.stroke(); + } + ctx.restore(); + + // Minute marks + ctx.save(); + ctx.lineWidth = 5; + for (i=0;i<60;i++){ + if (i%5!=0) { + ctx.beginPath(); + ctx.moveTo(117,0); + ctx.lineTo(120,0); + ctx.stroke(); + } + ctx.rotate(Math.PI/30); + } + ctx.restore(); + + var sec = now.getSeconds(); + var min = now.getMinutes(); + var hr = now.getHours(); + hr = hr>=12 ? hr-12 : hr; + + ctx.fillStyle = "black"; + + // write Hours + ctx.save(); + ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec ) + ctx.lineWidth = 14; + ctx.beginPath(); + ctx.moveTo(-20,0); + ctx.lineTo(80,0); + ctx.stroke(); + ctx.restore(); + + // write Minutes + ctx.save(); + ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec ) + ctx.lineWidth = 10; + ctx.beginPath(); + ctx.moveTo(-28,0); + ctx.lineTo(112,0); + ctx.stroke(); + ctx.restore(); + + // Write seconds + ctx.save(); + ctx.rotate(sec * Math.PI/30); + ctx.strokeStyle = "#D40000"; + ctx.fillStyle = "#D40000"; + ctx.lineWidth = 6; + ctx.beginPath(); + ctx.moveTo(-30,0); + ctx.lineTo(83,0); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0,0,10,0,Math.PI*2,true); + ctx.fill(); + ctx.beginPath(); + ctx.arc(95,0,10,0,Math.PI*2,true); + ctx.stroke(); + ctx.fillStyle = "rgba(0,0,0,0)"; + ctx.arc(0,0,3,0,Math.PI*2,true); + ctx.fill(); + ctx.restore(); + + ctx.beginPath(); + ctx.lineWidth = 14; + ctx.strokeStyle = '#325FA2'; + ctx.arc(0,0,142,0,Math.PI*2,true); + ctx.stroke(); + + ctx.restore(); +}</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">init();</pre> +</div> + +<p>{{EmbedLiveSample("An_animated_clock", "180", "180", "https://mdn.mozillademos.org/files/203/Canvas_animation2.png")}}</p> + +<h4 id="循環景色">循環景色</h4> + +<p>本例會產一個由左到右循環捲動<a href="http://commons.wikimedia.org/wiki/File:Capitan_Meadows,_Yosemite_National_Park.jpg" title="http://commons.wikimedia.org/wiki/File:Capitan_Meadows,_Yosemite_National_Park.jpg">美國優勝美地國家公園</a>景色,你也可以自行替換其他比畫布還大的圖片。</p> + +<pre class="brush: js">var img = new Image(); + +// User Variables - customize these to change the image being scrolled, its +// direction, and the speed. + +img.src = '/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg'; +var CanvasXSize = 800; +var CanvasYSize = 200; +var speed = 30; //lower is faster +var scale = 1.05; +var y = -4.5; //vertical offset + +// Main program + +var dx = 0.75; +var imgW; +var imgH; +var x = 0; +var clearX; +var clearY; +var ctx; + +img.onload = function() { + imgW = img.width*scale; + imgH = img.height*scale; + if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas + if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas + else { clearX = CanvasXSize; } + if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas + else { clearY = CanvasYSize; } + //Get Canvas Element + ctx = document.getElementById('canvas').getContext('2d'); + //Set Refresh Rate + return setInterval(draw, speed); +} + +function draw() { + //Clear Canvas + ctx.clearRect(0,0,clearX,clearY); + //If image is <= Canvas Size + if (imgW <= CanvasXSize) { + //reset, start from beginning + if (x > (CanvasXSize)) { x = 0; } + //draw aditional image + if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); } + } + //If image is > Canvas Size + else { + //reset, start from beginning + if (x > (CanvasXSize)) { x = CanvasXSize-imgW; } + //draw aditional image + if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); } + } + //draw image + ctx.drawImage(img,x,y,imgW,imgH); + //amount to move + x += dx; +} +</pre> + +<p>循環景色就是在下方的{{HTMLElement("canvas")}}中捲動,請注意其中的width和height和程式碼中的CanvasXZSize與CanvasYSize一樣。</p> + +<pre class="brush: html"><canvas id="canvas" width="800" height="200"></canvas></pre> + +<p><strong>Live sample</strong></p> + +<p>{{EmbedLiveSample("A_looping_panorama", "830", "230")}}</p> + +<h2 id="Other_examples" name="Other_examples">其他範例</h2> + +<dl> + <dt><a class="external" href="http://www.gartic.net/" title="http://www.gartic.net/">Gartic</a></dt> + <dd> + <p>多人繪圖遊戲</p> + </dd> + <dt><a class="external" href="http://www.abrahamjoffe.com.au/ben/canvascape/">Canvascape</a></dt> + <dd> + <p>第一人稱3D冒險遊戲</p> + </dd> + <dt><a href="/en-US/docs/Web/Guide/HTML/A_basic_ray-caster" title="/en-US/docs/Web/Guide/HTML/A_basic_ray-caster">A basic ray-caster</a></dt> + <dd>透過鍵盤控制動畫範例</dd> + <dt><a class="external" href="http://andrewwooldridge.com/canvas/canvasgame001/canvasgame002.html">canvas adventure</a></dt> + <dd> + <p>另一個透過鍵盤控制動畫範例</p> + </dd> + <dt><a class="external" href="http://www.blobsallad.se/">An interactive Blob</a></dt> + <dd>和Blob遊戲</dd> + <dt><a class="external" href="http://arapehlivanian.com/wp-content/uploads/2007/02/canvas.html">Flying through a starfield</a></dt> + <dd> + <p>飛越星河</p> + </dd> + <dt><a class="external" href="http://igrapher.com/" title="http://igrapher.com/">iGrapher</a></dt> + <dd> + <p>股票市場圖</p> + </dd> +</dl> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="/en-US/docs/JavaScript/Timers" title="/en-US/docs/JavaScript/Timers">JavaScript timers</a></li> + <li><a href="/en-US/docs/DOM/window.setInterval#A_little_framework" title="/en-US/docs/DOM/window.setInterval#A_little_framework"><code>setInterval</code> – A little framework</a></li> + <li><a href="/en-US/docs/JavaScript/Timers/Daemons" title="/en-US/docs/JavaScript/Timers/Daemons">JavaScript Daemons Management</a></li> + <li><a href="/en-US/docs/DOM/HTMLCanvasElement" title="/en-US/docs/DOM/HTMLCanvasElement">HTMLCanvasElement</a></li> +</ul> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Compositing", "Web/Guide/HTML/Canvas_tutorial/Optimizing_canvas")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/basic_usage/index.html b/files/zh-tw/web/api/canvas_api/tutorial/basic_usage/index.html new file mode 100644 index 0000000000..9d39027c96 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/basic_usage/index.html @@ -0,0 +1,158 @@ +--- +title: Canvas 基本用途 +slug: Web/API/Canvas_API/Tutorial/Basic_usage +translation_of: Web/API/Canvas_API/Tutorial/Basic_usage +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial", "Web/API/Canvas_API/Tutorial/Drawing_shapes")}}</div> + +<div class="summary"> +<p>Let's start this tutorial by looking at the {{HTMLElement("canvas")}} {{Glossary("HTML")}} element itself. At the end of this page, you will know how to set up a canvas 2D context and have drawn a first example in your browser.</p> +</div> + +<h2 id="<canvas>_元素"><code><canvas></code> 元素</h2> + +<pre class="brush: html"><canvas id="tutorial" width="150" height="150"></canvas> +</pre> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">首先,先來看看 </span></font>{{HTMLElement("canvas")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,它看起來有點像 </span></font>{{HTMLElement("img")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> 元素,其中的差異點在於 </span></font><code><canvas></code><font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> 沒有 </span></font><code>src</code><font face="DejaVu Sans, sans-serif"> <span lang="zh-TW">和</span> </font><code>alt</code><font face="DejaVu Sans, sans-serif"> <span lang="zh-TW">屬性,</span></font><code><canvas></code><font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> 只有 </span></font>{{htmlattrxref("width", "canvas")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> </span><span lang="zh-TW">與 </span></font>{{htmlattrxref("height", "canvas")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> 這兩個屬性,這兩個屬性皆為非必須、能透過</span></font><a href="/en-US/docs/DOM" rel="internal" title="en/DOM"> DOM</a><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">屬性設定;若是沒有設定 </span></font><code>width</code><font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> 和 </span></font><code>height</code><font face="DejaVu Sans, sans-serif"><span lang="zh-TW"> 屬性,畫布寬預設值為</span></font><strong><font face="DejaVu Sans, sans-serif"> </font>300 pixels</strong><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">、高預設值為<strong> </strong></span></font><strong>150 pixels</strong><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,我們可以用 </span></font><a href="/en-US/docs/Web/CSS" rel="internal" title="en/CSS">CSS </a><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">強制設定元素尺寸,但當渲染時,影像會縮放以符合元素的尺寸。</span></font></p> + +<div class="note"> +<p style="margin-bottom: 0in;"><strong>Note:</strong><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">如果繪圖結果看起來有些扭曲,可以改試著用</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">自身的</span></font><font face="DejaVu Sans, sans-serif">width</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">和</span></font><font face="DejaVu Sans, sans-serif">height</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">屬性而不要用</span></font><font face="DejaVu Sans, sans-serif">CSS</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">來設定寬高。</span></font></p> +</div> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">幾乎所有</span></font><font face="DejaVu Sans, sans-serif">HTML</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素都有</span></font><font face="DejaVu Sans, sans-serif">id</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">屬性,</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">也不例外,為了方便於程式碼腳本找到需要的</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,每次都設定</span></font><font face="DejaVu Sans, sans-serif">id</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">是一項不錯的作法。</span></font></p> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">如同一般的影像可以設定如邊界</span></font><font face="DejaVu Sans, sans-serif">(margin)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">、邊框</span></font><font face="DejaVu Sans, sans-serif">(border)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">、背景</span></font><font face="DejaVu Sans, sans-serif">(background)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">等等,</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素一樣可以設定這些樣式,然而,這些樣式規則不會影響</span></font><font face="DejaVu Sans, sans-serif">canvas</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">實際繪圖,稍後我們會看到相關範例。當沒有套用樣式規定時,</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">會被初始成全透明。</span></font></p> + +<p style="margin-bottom: 0in;"> </p> + +<div id="section_2"> +<h3 id="錯誤替代內容(Fallback_content)"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">錯誤替代內容(</span></font>Fallback content)</h3> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">因為舊版瀏覽器</span></font><font face="DejaVu Sans, sans-serif">(</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">特別是</span></font><font face="DejaVu Sans, sans-serif">IE9</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">之前的</span></font><font face="DejaVu Sans, sans-serif">IE)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">不支援</span></font><font face="DejaVu Sans, sans-serif">{<canvas>}</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素,我們應該為這些瀏覽器準備錯誤替代內容。</span></font></p> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">當不支援</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">的瀏覽器看到不認識的</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">時會忽略</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,而此時在</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">下瀏覽器認識的替代內容則會被瀏覽器解析顯示,至於支援</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">的瀏覽器則是會正常解析</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,忽略替代內容。</span></font></p> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">例如,我們可以準備一段</span></font><font face="DejaVu Sans, sans-serif">canvas</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">內容的說明文字或</span></font><font face="DejaVu Sans, sans-serif">canvas</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">繪圖完成後的靜態圖片,如下所示</span></font><font face="DejaVu Sans, sans-serif">:</font></p> + +<pre class="brush: html"><canvas id="stockGraph" width="150" height="150"> + current stock price: $3.15 +0.15 +</canvas> + +<canvas id="clock" width="150" height="150"> + <img src="images/clock.png" width="150" height="150" alt=""/> +</canvas> +</pre> + +<h6 id="sect1"> </h6> + +<h2 id="需要<canvas>標籤"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">需要</span></font><font face="DejaVu Sans, sans-serif"></canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">標籤</span></font></h2> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">不像</font></span></font>{{HTMLElement("img")}}<font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">元素,</font></span></font><font face="DejaVu Sans, sans-serif">{{HTMLElement("canvas")}}</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">元素必須要有</font></span></font><font face="DejaVu Sans, sans-serif"></canvas></font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">結束標籤。</font></span></font></p> + +<p style="margin-bottom: 0in;"> </p> + +<div class="note"> +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">縱使早期</span></font><font face="DejaVu Sans, sans-serif">Apple</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">的</span></font><font face="DejaVu Sans, sans-serif">Safari</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">瀏覽器不需要結束標籤,但是基於規範,這是必須的,所以,為了相容性考量,應該要有結束標籤。</span></font><font face="DejaVu Sans, sans-serif">Safari 2.0</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">以前的版本會同時解析</span></font><font face="DejaVu Sans, sans-serif">canvas</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">以及替代內容,除非我們用</span></font><font face="DejaVu Sans, sans-serif">CSS</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">去遮蓋內容,不過幸運的是,現在已經沒有甚麼人在用這些舊版</span></font><font face="DejaVu Sans, sans-serif">Safari</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">。</span></font></p> +</div> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">如果不需要錯誤替代內容,簡單的</span></font><font face="DejaVu Sans, sans-serif"><canvas id="foo" ...></canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">便可以完全相容於所有支援的瀏覽器。</span></font></p> + +<p style="margin-bottom: 0in;"> </p> + +<h2 id="渲染環境(rendering_context)"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">渲染環境</span></font>(rendering context)</h2> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif">{{HTMLElement("canvas")}}</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">產生一個固定大小的繪圖畫布,這個畫布上有一或多個</span></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">渲染環境</span></font><font face="DejaVu Sans, sans-serif">(rendering context),我們</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">可以用</span></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">渲染環境</span></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">來產生或操作顯示內容的渲染環境</span></font><font face="DejaVu Sans, sans-serif">(rendering context)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">。</span></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">不同</font>環境</span></font><font face="DejaVu Sans, sans-serif">(context)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">可能會提供不同型態的渲染方式,好比說</span></font><a href="/en-US/docs/Web/WebGL" title="/en-US/docs/Web/WebGL">WebGL</a><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">使用</span></font><a class="external" href="http://www.khronos.org/opengles/" rel="external" title="http://en.wikipedia.org/wiki/OpenGL_ES">OpenGL ES</a><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">的</span></font><font face="DejaVu Sans, sans-serif">3D</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">環境</span></font><font face="DejaVu Sans, sans-serif">(context),</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">而這裡我們主要將討論</span></font><font face="DejaVu Sans, sans-serif">2D</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">渲染環境</span></font><font face="DejaVu Sans, sans-serif">(rendering context)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">。</span></font></p> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">一開始</font></span></font><font face="DejaVu Sans, sans-serif">canvas</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">為空白,程式碼腳本需要先存取渲染環境,在上面繪圖,然後才會顯現影像。</font></span></font>{{HTMLElement("canvas")}} <font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">素有一個方法</font></span></font><font face="DejaVu Sans, sans-serif">(</font><a href="/en-US/docs/Web/API/HTMLCanvasElement#Methods" title="/en-US/docs/Web/API/HTMLCanvasElement#Methods">method</a><font face="DejaVu Sans, sans-serif">)</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">叫</font></span></font><font face="DejaVu Sans, sans-serif">getContext()</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">,透過此方法可以取得渲染環境及其繪圖函數</font></span></font><font face="DejaVu Sans, sans-serif">(function)</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">;</font></span></font><font face="DejaVu Sans, sans-serif">getContext()</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">輸入參數只有渲染環境類型一項,像本教學所討論的</font></span></font><font face="DejaVu Sans, sans-serif">2D</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">繪圖,就是輸入”</font></span></font><font face="DejaVu Sans, sans-serif">2d”</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">。</font></span></font></p> + +<pre class="brush: js">var canvas = document.getElementById('tutorial'); +var ctx = canvas.getContext('2d'); +</pre> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">上面第一行先呼叫</span></font>{{domxref("document.getElementById()")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW">來取得</span></font>{{HTMLElement("canvas")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素,一旦取得元素後,便可以用其</span></font><font face="DejaVu Sans, sans-serif">getContext()</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">取得渲染環境。</span></font></p> + +<p style="margin-bottom: 0in;"> </p> + +<div id="section_5"> +<h2 id="支援性檢查"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">支援性檢查</span></font></h2> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">替代內容會被不支援</span></font>{{HTMLElement("canvas")}}.<font face="DejaVu Sans, sans-serif"><span lang="zh-TW">的瀏覽器所顯示。程式碼腳本也可以利用檢查</span></font><font face="DejaVu Sans, sans-serif">getContext()</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">方法是否存在來檢查是否支援</span></font><font face="DejaVu Sans, sans-serif"><canvas></font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,我們可以修改上面例子成如下</span></font><font face="DejaVu Sans, sans-serif">:</font></p> + +<pre class="brush: js">var canvas = document.getElementById('tutorial'); + +if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + // drawing code here +} else { + // canvas-unsupported code here +} +</pre> +</div> +</div> + +<h6 id="sect2"> </h6> + +<h2 id="一個範本"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">一個範本</span></font></h2> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">這裡是一個最簡單的範本,之後就是我們範例的起始點。</span></font></p> + +<pre class="brush: html"><html> + <head> + <title>Canvas tutorial</title> + <script type="text/javascript"> + function draw(){ + var canvas = document.getElementById('tutorial'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + } + } + </script> + <style type="text/css"> + canvas { border: 1px solid black; } + </style> + </head> + <body onload="draw();"> + <canvas id="tutorial" width="150" height="150"></canvas> + </body> +</html> +</pre> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">一旦網頁載入完成後,程式碼會呼叫</span></font><font face="DejaVu Sans, sans-serif">draw()</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">函數</span></font><font face="DejaVu Sans, sans-serif">(</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">這是利用</span></font><font face="DejaVu Sans, sans-serif">document</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">上的</span></font><font face="DejaVu Sans, sans-serif">load</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">事件完成</span></font><font face="DejaVu Sans, sans-serif">)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,這類</span></font><font face="DejaVu Sans, sans-serif">draw()</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">函數也可以透過</span></font>{{domxref("window.setTimeout()")}}<font face="DejaVu Sans, sans-serif">, </font>{{domxref("window.setInterval()")}}<font face="DejaVu Sans, sans-serif"><span lang="zh-TW">或其他事件處理函數來呼叫,只要呼叫的時間點是在網頁載入完後。</span></font></p> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">這是我們的範本實際看起來的樣子</span></font><font face="DejaVu Sans, sans-serif">:</font></p> + +<p>{{EmbedLiveSample("A_skeleton_template", 160, 160)}}</p> + +<h2 id="一個簡單的範例"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">一個簡單的範例</span></font></h2> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">首先,讓我們先來畫兩個相交的正方形,其中一個正方形有</span></font><font face="DejaVu Sans, sans-serif">alpha</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">透明值,之後我們會說明這是如何達成的。</span></font></p> + +<pre class="brush: html"><html> + <head> + <script type="application/javascript"> + function draw() { + var canvas = document.getElementById("canvas"); + if (canvas.getContext) { + var ctx = canvas.getContext("2d"); + + ctx.fillStyle = "rgb(200,0,0)"; + ctx.fillRect (10, 10, 55, 50); + + ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; + ctx.fillRect (30, 30, 55, 50); + } + } + </script> + </head> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">本範例的結果如下</span></font><font face="DejaVu Sans, sans-serif">:</font></p> + +<p>{{EmbedLiveSample("A_simple_example", 160, 160, "https://mdn.mozillademos.org/files/228/canvas_ex1.png")}}</p> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial", "Web/Guide/HTML/Canvas_tutorial/Drawing_shapes")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/compositing/index.html b/files/zh-tw/web/api/canvas_api/tutorial/compositing/index.html new file mode 100644 index 0000000000..e5453c93a5 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/compositing/index.html @@ -0,0 +1,207 @@ +--- +title: 合成效果 +slug: Web/API/Canvas_API/Tutorial/Compositing +translation_of: Web/API/Canvas_API/Tutorial/Compositing +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Transformations", "Web/API/Canvas_API/Tutorial/Basic_animations")}}</div> + +<p>在前述的範例中,新繪製的圖形總會覆蓋在之前的圖形上,對大多數情況來說這相當正常,不過它也限制了圖形繪製的順序。其實我們可以透過 globalCompositeOperation 屬性來改變這項預設行為。</p> + +<h2 id="globalCompositeOperation" name="globalCompositeOperation"><code>globalCompositeOperation</code></h2> + +<p>利用 globalCompositeOperation,我們可以將新圖形繪製在舊圖形之下、遮蓋部分區域、清除畫布部分區域 (不同於 clearRect() 函式只能清除矩形區域)。</p> + +<dl> + <dt><code>globalCompositeOperation = <em>type</em></code></dt> + <dt>type 字串可指定為以下 12 種合成設定之一,每一種合成設定均將套用到新繪製的圖形上。</dt> +</dl> + +<div class="note"> +<p><strong>Note:</strong> 下列圖例的藍色矩形是舊圖形,紅色圓形是新圖形。</p> +</div> + +<table style="width: 100%;"> + <tbody> + <tr> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>source-over</strong> 預設值。將新圖形畫在舊圖形之上。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_srcovr.png" class="internal" src="https://mdn.mozillademos.org/files/220/Canvas_composite_srcovr.png"></p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>destination-over</strong><br> + 將新圖形畫在舊圖形之下。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_destovr.png" class="internal" src="https://mdn.mozillademos.org/files/215/Canvas_composite_destovr.png"></p> + </td> + </tr> + <tr> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>source-in</strong><br> + 只保留新、舊圖形重疊的新圖形區域,其餘皆變為透明。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_srcin.png" class="internal" src="https://mdn.mozillademos.org/files/218/Canvas_composite_srcin.png"></p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>destination-in</strong><br> + 只保留新、舊圖形重疊的舊圖形區域,其餘皆變為透明。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_destin.png" class="internal" src="https://mdn.mozillademos.org/files/213/Canvas_composite_destin.png"></p> + </td> + </tr> + <tr> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>source-out</strong><br> + 只保留新、舊圖形非重疊的新圖形區域,其餘皆變為透明。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_srcout.png" class="internal" src="https://mdn.mozillademos.org/files/219/Canvas_composite_srcout.png"></p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>destination-out</strong><br> + 只保留新、舊圖形非重疊的舊圖形區域,其餘皆變為透明。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_destout.png" class="internal" src="https://mdn.mozillademos.org/files/214/Canvas_composite_destout.png"></p> + </td> + </tr> + <tr> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>source-atop</strong><br> + 新圖形只繪製在新、舊圖形重疊的新圖形區域,然後蓋在舊圖形之上。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_srcatop.png" class="internal" src="https://mdn.mozillademos.org/files/217/Canvas_composite_srcatop.png"></p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>destination-atop</strong><br> + 舊圖形只保留在新、舊圖形重疊的舊圖形區域,然後蓋在新圖形之上。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_destatop.png" class="internal" src="https://mdn.mozillademos.org/files/212/Canvas_composite_destatop.png"></p> + </td> + </tr> + <tr> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>lighter</strong><br> + 新舊圖形重疊區域的顏色,由新、舊圖形的顏色碼相加而得。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_lighten.png" class="internal" src="https://mdn.mozillademos.org/files/216/Canvas_composite_lighten.png"></p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>darker</strong> {{obsolete_inline}}</p> + + <p>新舊圖形重疊區域的顏色,由新、舊圖形的顏色碼相減而得。此屬性值已經從畫布規格中移除了,不再支援。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_darken.png" class="internal" src="https://mdn.mozillademos.org/files/211/Canvas_composite_darken.png"></p> + </td> + </tr> + <tr> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>xor</strong><br> + 新舊圖形重疊區域設為透明。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_xor.png" class="internal" src="https://mdn.mozillademos.org/files/221/Canvas_composite_xor.png"></p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><strong>copy</strong><br> + 移除其他圖形,只保留新圖形。</p> + </td> + <td style="padding: 5px; vertical-align: top;"> + <p><img alt="Image:Canvas_composite_copy.png" class="internal" src="https://mdn.mozillademos.org/files/210/Canvas_composite_copy.png"></p> + </td> + </tr> + </tbody> +</table> + +<p>這裡有這些構圖組合的<a href="/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing/Example" title="/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing/Example">實際範例</a>,<a href="https://mdn.mozillademos.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing/Example$samples/Compositing_example" title="https://mdn.mozillademos.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing/Example$samples/Compositing_example">輸出結果</a>在此。</p> + +<h2 id="Clipping_paths" name="Clipping_paths">裁剪路徑</h2> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/209/Canvas_clipping_path.png" style="float: right;">裁剪路徑就像是一般畫布圖形繪圖,但就如同遮罩一樣,會蓋掉不需要的部分,如右圖所示。紅邊星星是我們的裁剪路徑,在路徑區域以外部分都不會出現在畫布上。</p> + +<p>和上述 globalCompositeOperation 相比,可以發現 source-in 和 source-atop 這兩種構圖組合所達到的效果,和裁剪路徑類似,而其中最大差異在於裁剪路徑不需加入新圖形,消失的部分也不會出現在畫布上,所以,如果想要限定繪圖區域,裁剪路徑會是更理想的作法。</p> + +<p>在<a href="/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes#Drawing_paths" title="Web/Guide/HTML/Canvas_tutorial/Drawing_shapes#Drawing_paths">繪畫圖形</a>一章中,我們只提到 stroke() 和 fill() 函式,但其實還有第三個函式,那就是 clip() 函式。</p> + +<dl> + <dt><code>clip()</code></dt> + <dd>轉換目前繪圖路徑為裁剪路徑。</dd> +</dl> + +<p>呼叫 clip() 除了會替代 closePath() 來關閉路徑之外,還會轉換目前填滿或勾勒繪圖路徑為裁剪路徑。</p> + +<p> {{HTMLElement("canvas")}} 畫布預設有一個等同於本身大小的裁剪路徑,等同於無裁剪效果。</p> + +<h3 id="A_clip_example" name="A_clip_example"><code>clip</code> 範例</h3> + +<p>本範例使用了圓形的裁剪路徑,來限定畫星星時的繪圖區域。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + ctx.fillRect(0,0,150,150); + ctx.translate(75,75); + + // Create a circular clipping path + ctx.beginPath(); + ctx.arc(0,0,60,0,Math.PI*2,true); + ctx.clip(); + + // draw background + var lingrad = ctx.createLinearGradient(0,-75,0,75); + lingrad.addColorStop(0, '#232256'); + lingrad.addColorStop(1, '#143778'); + + ctx.fillStyle = lingrad; + ctx.fillRect(-75,-75,150,150); + + // draw stars + for (var j=1;j<50;j++){ + ctx.save(); + ctx.fillStyle = '#fff'; + ctx.translate(75-Math.floor(Math.random()*150), + 75-Math.floor(Math.random()*150)); + drawStar(ctx,Math.floor(Math.random()*4)+2); + ctx.restore(); + } + +} + +function drawStar(ctx,r){ + ctx.save(); + ctx.beginPath() + ctx.moveTo(r,0); + for (var i=0;i<9;i++){ + ctx.rotate(Math.PI/5); + if(i%2 == 0) { + ctx.lineTo((r/0.525731)*0.200811,0); + } else { + ctx.lineTo(r,0); + } + } + ctx.closePath(); + ctx.fill(); + ctx.restore(); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>一開始我們先畫了一個黑色矩形作為畫布背景,然後移動畫布原點到中央,接著我們繪製弧線並呼叫 clip(),藉以建立圓形的裁剪路徑。畫布儲存狀態亦可儲存裁剪路徑。若要保留原本的裁剪路徑,則可於繪製新的裁剪路徑之前,先行儲存畫布狀態。</p> + +<p>繪製裁剪路徑之後,所產生的所有圖形都只會出現在路徑以內,從後來繪製的漸層背景中可看出此特性。我們用自訂的 drawStar() 函數產生 50 個隨機散佈、大小不一的星星。這些星星同樣只會出現在裁剪路徑的範圍之內。</p> + +<p>{{EmbedLiveSample("A_clip_example", "180", "180", "https://mdn.mozillademos.org/files/208/Canvas_clip.png")}}</p> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Transformations", "Web/Guide/HTML/Canvas_tutorial/Basic_animations")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/drawing_shapes/index.html b/files/zh-tw/web/api/canvas_api/tutorial/drawing_shapes/index.html new file mode 100644 index 0000000000..96fe2b9615 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/drawing_shapes/index.html @@ -0,0 +1,551 @@ +--- +title: 繪製圖形 +slug: Web/API/Canvas_API/Tutorial/Drawing_shapes +translation_of: Web/API/Canvas_API/Tutorial/Drawing_shapes +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}</div> + +<p id="網格(Grid)">網格(Grid)</p> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/224/Canvas_default_grid.png" style="float: right; height: 220px; width: 220px;">在開始繪圖前,我們必須先了解畫布 (canvas) 網格,或著是說座標空間。在前一頁教學中的 HTML 範本有一個寬150 pixels (像素)、高150 pixels 的畫布。如右圖,你在畫布預設網格上繪圖,網格上 1 單位相當於畫布上 1 pixel,網格的原點 (座標 (0, 0) ) 坐落於左上角,所有元素定位皆相對於此左上角原點,所以藍色方塊的位置為從左往右推 x pixels、從上往下推 y pixels (亦即座標 (x, y) )。現在我們先專注在預設設定上,之後我們會看到如何轉換原點位置、旋轉網格以及縮放網格。</p> + +<p> </p> + +<p> </p> + +<p> </p> + +<h2 id="畫矩形">畫矩形</h2> + +<p>不同於<a href="/en-US/docs/SVG" rel="internal" title="en/SVG">SVG</a>,{{HTMLElement("canvas")}}只支援一種原始圖形,矩形。所有的圖形都必須由一或多個繪圖路徑構成,而我們正好有一些繪圖路徑函數可以讓我們畫出複雜的圖形。</p> + +<div id="section_3"> +<p>首先來看看矩形,共有三個矩形繪圖函數:</p> + +<dl> + <dt><code>fillRect(<em>x</em>, <em>y</em>, <em>width</em>, <em>height</em>)</code></dt> + <dd>畫出一個填滿的矩形。</dd> + <dt><code>strokeRect(<em>x</em>, <em>y</em>, <em>width</em>, <em>height</em>)</code></dt> + <dd>畫出一個矩形的邊框</dd> + <dt><code>clearRect(<em>x</em>, <em>y</em>, <em>width</em>, <em>height</em>)</code></dt> + <dd>清除指定矩形區域內的內容,使其變為全透明。</dd> +</dl> + +<p>這三個函數都接受一樣的參數: x, y代表從原點出發的座標位置,width, height代表矩形的寬高。</p> + +<h3 id="矩形範例">矩形範例</h3> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext) { + var ctx = canvas.getContext('2d'); + + ctx.fillRect(25,25,100,100); + ctx.clearRect(45,45,60,60); + ctx.strokeRect(50,50,50,50); + } +}</pre> + +<p>本例結果如下:</p> + +<p>{{EmbedLiveSample("Rectangular_shape_example", 160, 160, "https://mdn.mozillademos.org/files/245/Canvas_rect.png")}}</p> + +<p>fillRect()函數畫出一個寬高都100 pixels的矩形,clearRect()函數清除中央60 x 60 pixels大的正方形區域,接著strokeRect()在被清除區域內畫上一個50 x 50 pixels的矩形邊框。</p> + +<p>之後我們會看到另外兩種代替clearRect()的方法,還有如何改變圖形顏色與筆畫樣式。</p> + +<p>不像之後會看到的路徑繪圖函數,這三個函數會立即在畫布上畫出矩形。</p> + +<h2 id="路徑繪製">路徑繪製</h2> + +<p>使用路徑 (path) 來畫圖形需要多一點步驟,一開始先產生路徑,然後用繪圖指令畫出路徑,然後再結束路徑,一旦路徑產生後便可以用畫筆或填滿方式來渲染生成,這裡是一些可用函數:</p> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.beginPath", "beginPath()")}}</dt> + <dd>產生一個新路徑,產生後再使用繪圖指令來設定路徑。</dd> + <dt>{{domxref("CanvasRenderingContext2D.closePath", "closePath()")}}</dt> + <dd>閉合路徑好讓新的繪圖指令來設定路徑。</dd> + <dt><a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#Paths">路徑 API</a></dt> + <dd>路徑 API,這些 API 便是繪圖指令</dd> +</dl> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.stroke", "stroke()")}}</dt> + <dd>畫出圖形的邊框。</dd> + <dt>{{domxref("CanvasRenderingContext2D.fill", "fill()")}}</dt> + <dd>填滿路徑內容區域來產生圖形。</dd> +</dl> + +<p>第一步呼叫 beginPath() 產生一個路徑,表面下,路徑會被存在一個次路徑 (sub-path) 清單中,例如直線、曲線等,這些次路徑集合起來就形成一塊圖形。每一次呼叫這個方法,次路徑清單就會被重設,然後我們便能夠畫另一個新圖形。</p> + +<div class="note"><strong>Note:</strong> 當目前路徑為空(例如接著呼叫beginPath()完後)或是在一個新畫布上,不論為何,第一個路徑繪圖指令總是moveTo();因為每當重設路徑後,你幾乎都會需要設定繪圖起始點。</div> + +<p>第二步是呼叫各式方法來實際設定繪圖路徑,稍後我們將會介紹這部分。</p> + +<p>第三步,也是非必要的一步,就是呼叫closePath()。這個方法會在現在所在點到起始點間畫一條直線以閉合圖形,如果圖形已經閉合或是只含一個點,這個方法不會有任何效果。</p> + +<div class="note"><strong>Note:</strong> 當呼叫fill(),任何開放的圖形都會自動閉合,所以不需要再呼叫closePath(),但是stroke()並非如此。</div> + +<h3 id="畫一個三角形">畫一個三角形</h3> + +<p>這是一個畫出三角形的程式碼範例。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + ctx.beginPath(); + ctx.moveTo(75,50); + ctx.lineTo(100,75); + ctx.lineTo(100,25); + ctx.fill(); + } +} +</pre> + +<p>結果如下:</p> + +<p>{{EmbedLiveSample("Drawing_a_triangle", 110, 110, "https://mdn.mozillademos.org/files/9847/triangle.png")}}</p> + +<h3 id="移動畫筆">移動畫筆</h3> + +<p>moveTo()是一個很有用的函數,moveTo()不會畫任何圖形,但卻是上述路徑清單的一部分,這大概有點像是把筆從紙上一點提起來,然後放到另一個點。</p> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.moveTo", "moveTo(x, y)")}}</dt> + <dd>移動畫筆到指定的(x, y)座標點</dd> +</dl> + +<p>當初始化畫布或是呼叫beginPath(),通常會想要使用moveTo()來指定起始點,我們可以用moveTo()畫不連結的路徑,看一下笑臉圖範例,圖中紅線即為使用到moveTo()的位置。</p> + +<p>你可以拿下面的程式碼,放進先前的draw()函數,自己試試看效果。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + ctx.beginPath(); + ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle + ctx.moveTo(110,75); + ctx.arc(75,75,35,0,Math.PI,false); // Mouth (clockwise) + ctx.moveTo(65,65); + ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye + ctx.moveTo(95,65); + ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye + ctx.stroke(); + } +} +</pre> + +<p>結果如下:</p> + +<p>{{EmbedLiveSample("Moving_the_pen", 160, 160, "https://mdn.mozillademos.org/files/252/Canvas_smiley.png")}}</p> + +<p>移除moveTo()便可以看到線條連結起來。</p> + +<div class="note"> +<p><strong>Note:</strong> 有關arc(),請參照下方 {{anch("Arcs")}} .</p> +</div> + +<h3 id="線條">線條</h3> + +<p>用lineTo()方法畫直線。</p> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.lineTo", "lineTo(x, y)")}}</dt> + <dd>從目前繪圖點畫一條直線到指定的(x, y)座標點。</dd> +</dl> + +<p>本方法接受x, y參數作為線條結束點的座標位置,至於起始點則視前一個繪圖路徑,由前一個繪圖路徑的結束點作為起始點,當然,起始點也可以用moveTo()方法來變更。</p> + +<p>下面畫兩個三角形,一個填滿,一個空心。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + // Filled triangle + ctx.beginPath(); + ctx.moveTo(25,25); + ctx.lineTo(105,25); + ctx.lineTo(25,105); + ctx.fill(); + + // Stroked triangle + ctx.beginPath(); + ctx.moveTo(125,125); + ctx.lineTo(125,45); + ctx.lineTo(45,125); + ctx.closePath(); + ctx.stroke(); + } +} +</pre> + +<p>從呼叫beginPath()起始一個新圖形路徑,然後用moveTo()移到我們想要的起始點,然後再畫兩條線形成三角形的兩邊。</p> + +<p>{{EmbedLiveSample("Lines", 160, 160, "https://mdn.mozillademos.org/files/238/Canvas_lineTo.png")}}</p> + +<p>我們可以看到填滿(fill)三角形和勾勒(stroke)三角形的區別;當填滿時,圖形會自動閉合,不過勾勒則不會,所以如果沒有呼叫closePaht()的話,只會畫出兩條線而非三角形。</p> + +<h3 id="弧形">弧形</h3> + +<p>用arc()方法來畫弧形或圓形。雖然也可以用arcTo(),但這個方法比較不可靠,所以這裡我們不討論arcTo()。</p> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.arc", "arc(x, y, radius, startAngle, endAngle, anticlockwise)")}}</dt> + <dd>畫一個弧形</dd> +</dl> + +<p>本方法接受五個參數: x, y代表圓心座標點,radius代表半徑,startAngle, endAngle分別代表沿著弧形曲線上的起始點與結束點的弧度,弧度測量是相對於x軸,anticlockwise為true代表逆時針作圖、false代表順時針作圖。</p> + +<div class="note"> +<p><strong>Note</strong>: arc()方法用的是弧度(radians)而非角度(degrees),如果要在弧度與角度間換算,可以利用以下javascript程式碼: radians = (Math.PI/180) * degrees.</p> +</div> + +<p>以下例子比較複雜,它會畫出12個不同的弧形。</p> + +<p>兩個for迴圈走一遍弧形圖列的列跟行,每一個弧形由呼叫beginPath()開始新的繪圖路徑,為了清楚,我們在程式範例中用變數儲存參數,你不一定要這麼做。</p> + +<p>x, y座標點的部分應該相當淺顯,radius和startAngle是定值,endAngle從180度(半圓)開始,然後每一行增加90度,最後一行便會形成一個完整的圓。</p> + +<p>第1, 3列的anticlockwise 為false,所以會順時針作圖,2, 4列的anticlockwise 為true,所以會逆時針作圖。最後的if決定下半部是用填滿圖形,上半部是勾勒圖形。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="200"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + for(var i=0;i<4;i++){ + for(var j=0;j<3;j++){ + ctx.beginPath(); + var x = 25+j*50; // x coordinate + var y = 25+i*50; // y coordinate + var radius = 20; // Arc radius + var startAngle = 0; // Starting point on circle + var endAngle = Math.PI+(Math.PI*j)/2; // End point on circle + var anticlockwise = i%2==0 ? false : true; // clockwise or anticlockwise + + ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); + + if (i>1){ + ctx.fill(); + } else { + ctx.stroke(); + } + } + } + } +} +</pre> +{{EmbedLiveSample("Arcs", 160, 210, "https://mdn.mozillademos.org/files/204/Canvas_arc.png")}} + +<h3 id="貝茲曲線(Bezier_curve)與二次曲線(quadratic_curve)">貝茲曲線(Bezier curve)與二次曲線(quadratic curve)</h3> + +<p>二次與三次<a href="http://en.wikipedia.org/wiki/B%C3%A9zier_curve" title="http://en.wikipedia.org/wiki/B%C3%A9zier_curve"><span class="external">貝茲曲線(Bézier curves</span>)</a>是另一種可用來構成複雜有機圖形的路徑。</p> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.quadraticCurveTo", "quadraticCurveTo(cp1x, cp1y, x, y)")}}</dt> + <dd>從目前起始點畫一條二次貝茲曲線到x, y指定的終點,控制點由cp1x, cp1y指定。</dd> + <dt>{{domxref("CanvasRenderingContext2D.bezierCurveTo", "bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)")}}</dt> + <dd>從目前起始點畫一條三次貝茲曲線到x, y指定的終點,控制點由(cp1x, cp1y)和(cp2x, cp2y)指定。</dd> +</dl> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/223/Canvas_curves.png" style="float: right; height: 190px; width: 190px;">二次和三次的差別可以從右圖看出;貝茲曲線的起始和終點以藍點標示,其中二次貝茲曲線只有一個控制點(如紅點標示)而三次貝茲曲線有兩個控制點。</p> + +<p>二次和三次貝茲曲線都用x, y參數定義終點座標,然後用cp1x, xp1y定義第一個控制點座標、cp2x, xp2y定義第二個控制點座標。</p> + +<p>用二次和三次貝茲曲線作圖相當具有挑戰性,因為不像使用 Adobe illustrator 的向量繪圖軟體,我們在繪圖時無法即時看到繪圖狀況,所以畫複雜的圖形十分困難。下面的範例我們畫了一些圖形,如果你有時間與耐心,可以畫出更複雜的圖形。</p> + +<h4 id="二次貝茲曲線">二次貝茲曲線</h4> + +<p>本例用了數個二次貝茲曲線畫了一個會話框。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext) { + var ctx = canvas.getContext('2d'); + + // Quadratric curves example + ctx.beginPath(); + ctx.moveTo(75,25); + ctx.quadraticCurveTo(25,25,25,62.5); + ctx.quadraticCurveTo(25,100,50,100); + ctx.quadraticCurveTo(50,120,30,125); + ctx.quadraticCurveTo(60,120,65,100); + ctx.quadraticCurveTo(125,100,125,62.5); + ctx.quadraticCurveTo(125,25,75,25); + ctx.stroke(); + } +} +</pre> + +<p>{{EmbedLiveSample("Quadratic_Bezier_curves", 160, 160, "https://mdn.mozillademos.org/files/243/Canvas_quadratic.png")}}</p> + +<h4 id="三次貝茲曲線">三次貝茲曲線</h4> + +<p>這個範例畫了一個愛心。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + // Quadratric curves example + ctx.beginPath(); + ctx.moveTo(75,40); + ctx.bezierCurveTo(75,37,70,25,50,25); + ctx.bezierCurveTo(20,25,20,62.5,20,62.5); + ctx.bezierCurveTo(20,80,40,102,75,120); + ctx.bezierCurveTo(110,102,130,80,130,62.5); + ctx.bezierCurveTo(130,62.5,130,25,100,25); + ctx.bezierCurveTo(85,25,75,37,75,40); + ctx.fill(); + } +} +</pre> + +<p>{{EmbedLiveSample("Cubic_Bezier_curves", 160, 160, "https://mdn.mozillademos.org/files/207/Canvas_bezier.png")}}</p> + +<h3 id="矩形">矩形</h3> + +<p>除了在{畫矩形}段落中提到的三個方法,還有rect()方法能夠在畫布上畫矩形;rect()方法會在目前路徑下加入一個矩形繪圖路徑。</p> + +<dl> + <dt>{{domxref("CanvasRenderingContext2D.bezierCurveTo", "rect(x, y, width, height)")}}</dt> + <dd>畫一個左上角位於(x, y)、寬width、高height的矩形。</dd> +</dl> + +<p>呼叫這個方法,moveTo()方法會以(0, 0)參數被自動呼叫,所以目前的下筆點跟者自動被設為預設座標。</p> + +<h3 id="多樣組合">多樣組合</h3> + +<p>截至目前為止,我們都只用一種路徑函數在各個範例裡作圖,不過,其實繪圖時並沒有任何使用數量或種類上的路徑函數限制,所以最後我們來試著組合各樣路徑繪圖函數來畫一些十分有名的遊戲角色。</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + roundedRect(ctx,12,12,150,150,15); + roundedRect(ctx,19,19,150,150,9); + roundedRect(ctx,53,53,49,33,10); + roundedRect(ctx,53,119,49,16,6); + roundedRect(ctx,135,53,49,33,10); + roundedRect(ctx,135,119,25,49,10); + + ctx.beginPath(); + ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false); + ctx.lineTo(31,37); + ctx.fill(); + + for(var i=0;i<8;i++){ + ctx.fillRect(51+i*16,35,4,4); + } + + for(i=0;i<6;i++){ + ctx.fillRect(115,51+i*16,4,4); + } + + for(i=0;i<8;i++){ + ctx.fillRect(51+i*16,99,4,4); + } + + ctx.beginPath(); + ctx.moveTo(83,116); + ctx.lineTo(83,102); + ctx.bezierCurveTo(83,94,89,88,97,88); + ctx.bezierCurveTo(105,88,111,94,111,102); + ctx.lineTo(111,116); + ctx.lineTo(106.333,111.333); + ctx.lineTo(101.666,116); + ctx.lineTo(97,111.333); + ctx.lineTo(92.333,116); + ctx.lineTo(87.666,111.333); + ctx.lineTo(83,116); + ctx.fill(); + + ctx.fillStyle = "white"; + ctx.beginPath(); + ctx.moveTo(91,96); + ctx.bezierCurveTo(88,96,87,99,87,101); + ctx.bezierCurveTo(87,103,88,106,91,106); + ctx.bezierCurveTo(94,106,95,103,95,101); + ctx.bezierCurveTo(95,99,94,96,91,96); + ctx.moveTo(103,96); + ctx.bezierCurveTo(100,96,99,99,99,101); + ctx.bezierCurveTo(99,103,100,106,103,106); + ctx.bezierCurveTo(106,106,107,103,107,101); + ctx.bezierCurveTo(107,99,106,96,103,96); + ctx.fill(); + + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(101,102,2,0,Math.PI*2,true); + ctx.fill(); + + ctx.beginPath(); + ctx.arc(89,102,2,0,Math.PI*2,true); + ctx.fill(); + } +} + +// A utility function to draw a rectangle with rounded corners. + +function roundedRect(ctx,x,y,width,height,radius){ + ctx.beginPath(); + ctx.moveTo(x,y+radius); + ctx.lineTo(x,y+height-radius); + ctx.quadraticCurveTo(x,y+height,x+radius,y+height); + ctx.lineTo(x+width-radius,y+height); + ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius); + ctx.lineTo(x+width,y+radius); + ctx.quadraticCurveTo(x+width,y,x+width-radius,y); + ctx.lineTo(x+radius,y); + ctx.quadraticCurveTo(x,y,x,y+radius); + ctx.stroke(); +} +</pre> + +<div id="section_18"> +<p>結果如下:</p> + +<p>{{EmbedLiveSample("Making_combinations", 160, 160)}}</p> + +<p>畫出這樣的圖其實沒有想像中的困難,所以我們就不再描述細節了,其中比較需要注意的是,我們在繪圖環境上用了fillStyle屬性以及一個自定義的效用函數(roundedRect()),利用效用函數來執行時常重複的繪圖工作可以幫忙減少程式碼數量與複雜度。</p> + +<p>稍後我們會更進一步介紹fillStyle屬性,這個範例我們所做是的透過fillStyle屬性來改變路徑填滿色為白色,然後再改回預設黑色,來變換填滿顏色,。</p> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Basic_usage", "Web/Guide/HTML/Canvas_tutorial/Using_images")}}</p> +</div> +</div> + +<h2 id="Path2D_objects">Path2D objects</h2> + +<p>如同前面的範例,canvas 上常常會畫上一連串的繪圖路徑,為了簡化程式碼還有改善效能,我們可以利用 {{domxref("Path2D")}} 物件 (目前在較先進的瀏覽器上已經有提供了)。Path2D 讓我們可以快取和記錄繪圖指令,方便快速重複地繪圖,底下我就來看看如何使用 Path2D :</p> + +<p>{{domxref("Path2D.Path2D", "Path2D()")}}</p> + +<p> Path2D 的建構子,可接受的參數有無參數、另一個 Path2D 物件、 字元表式的 <a href="/en-US/docs/Web/SVG/Tutorial/Paths">SVG path</a>:</p> + +<pre class="brush: js">new Path2D(); // 不傳入參數會回傳一個空的 Path2D 物件 +new Path2D(path); // 複製傳入的 Path2D 物件,然後以之建立 Path2D 物件 +new Path2D(d); // 以傳入的 SVG 路徑建立 Path2D 物件</pre> + +<p>所有已知的 <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#Paths">路徑 API</a>,如 rect, arc 等等,都可以在 Path2D 上找到。</p> + +<p>Path2D 物件還可以加入其他 Path2D 物件,這讓我們可以很方便的組合多個物件使用。</p> + +<p>{{domxref("Path2D.addPath", "Path2D.addPath(path [, transform])")}}</p> + +<p> addPath 增加一個 Path2D 物件,其中的非必要參數是變形矩陣。</p> + +<h3 id="Path2D_example">Path2D example</h3> + +<p>這個例子用 Path2D 物件將矩形和圓形的繪圖路徑存起來,以供之後使用。而配合新的 Path2D API,有一些繪圖方法更接受傳入 Path2D 作為繪圖路徑使用,例如下方本例所用到的 stroke 和 fill。</p> + +<pre class="brush: js;highlight[6,9]">function draw() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext){ + var ctx = canvas.getContext('2d'); + + var rectangle = new Path2D(); + rectangle.rect(10, 10, 50, 50); + + var circle = new Path2D(); + circle.moveTo(125, 35); + circle.arc(100, 35, 25, 0, 2 * Math.PI); + + ctx.stroke(rectangle); + ctx.fill(circle); + } +}</pre> + +<p>{{EmbedLiveSample("Path2D_example", 130, 110, "https://mdn.mozillademos.org/files/9851/path2d.png")}}</p> + +<h3 id="使用向量路徑_(SVG_paths)">使用向量路徑 (SVG paths)</h3> + +<p>另一個強而有力的特色是在 SVG 和 Canvas 中我們都可以使用 SVG path。</p> + +<p>下面的路徑會移到座標點 (10, 10) (M10, 10),然後水平右移 80 點 (h 80),垂至下移 80 點 (v 80) 水平左移 80 點 (h -80) 最後回到起始點 (z),請到<a href="https://developer.mozilla.org/en-US/docs/Web/API/Path2D.Path2D#Using_SVG_paths"><code>Path2D</code> 建構子頁面</a>看繪圖範例結果。</p> + +<pre class="brush: js;">var p = new Path2D("M10 10 h 80 v 80 h -80 Z");</pre> + +<div>{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}</div> + +<p> </p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/drawing_text/index.html b/files/zh-tw/web/api/canvas_api/tutorial/drawing_text/index.html new file mode 100644 index 0000000000..cfafa8a5a8 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/drawing_text/index.html @@ -0,0 +1,397 @@ +--- +title: 使用canvas繪製文字 +slug: Web/API/Canvas_API/Tutorial/Drawing_text +translation_of: Web/API/Canvas_API/Tutorial/Drawing_text +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Applying_styles_and_colors", "Web/API/Canvas_API/Tutorial/Using_images")}}</div> + +<p>{{ gecko_minversion_header("1.9") }}</p> + +<p><code><a href="/en/HTML/Canvas" title="en/HTML/Canvas">canvas</a></code>元素支援在<a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/#text-0" title="http://www.whatwg.org/specs/web-apps/current-work/#text">標準 HTML 5 特色</a>以及少許實驗性的Mozilla方法和功能上繪製文字。</p> + +<p>文字可以包括任何Unicode字元,即使用那些超出“基本多文種平面”的字元也可以。</p> + +<p>{{ fx_minversion_note("3.5", '在Firefox 3.5或之後的版本,當繪圖時,任何對於 <a class="internal" href="/en/Canvas_tutorial/Applying_styles_and_colors#Shadows" title="En/Canvas tutorial/Applying styles and colors#Shadows">shadow effects</a>(陰影效果)的處理可以使用在文字上。') }}</p> + +<h2 id="Method_overview" name="Method_overview">方法概述</h2> + +<table class="standard-table"> + <tbody> + <tr> + <td><code>void <a class="internal" href="/en/Drawing_text_using_a_canvas#fillText()" title="en/Drawing text using a canvas#fillText()">fillText</a>(in <a class="internal" href="/En/DOM/DOMString" title="en/DOM/DOMString">DOMString</a> text, in float x, in float y, [optional] in float maxWidth);</code> {{ gecko_minversion_inline("1.9.1") }}</td> + </tr> + <tr> + <td><a class="internal" href="/nsIDOMTextMetrics" title="nsIDOMTextMetrics"><span style="font-family: monospace;">nsIDOMTextMetrics</span></a><code> <a class="internal" href="/en/Drawing_text_using_a_canvas#measureText()" title="en/Drawing text using a canvas#measureText()">measureText</a>(in DOMString textToMeasure);</code> {{ gecko_minversion_inline("1.9.1") }}</td> + </tr> + <tr> + <td><code>void <a href="#mozDrawText.28.29">mozDrawText</a>(in DOMString textToDraw); {{ deprecated_inline() }}</code></td> + </tr> + <tr> + <td><code>float <a href="#mozMeasureText.28.29">mozMeasureText</a>(in DOMString textToMeasure);</code><code> {{ deprecated_inline() }}</code></td> + </tr> + <tr> + <td><code>void <a href="#mozPathText.28.29">mozPathText</a>(in DOMString textToPath);</code></td> + </tr> + <tr> + <td><code>void <a href="#mozTextAlongPath.28.29">mozTextAlongPath</a>(in DOMString textToDraw, in boolean stroke);</code></td> + </tr> + <tr> + <td><code>void <a class="internal" href="/en/Drawing_text_using_a_canvas#strokeText()" title="en/Drawing text using a canvas#strokeText()">strokeText</a>(in DOMString text, in float x, in float y, [optional] in float maxWidth);</code> {{ gecko_minversion_inline("1.9.1") }}</td> + </tr> + </tbody> +</table> + +<p> </p> + +<h2 id="Attributes" name="Attributes">屬性</h2> + +<table class="standard-table"> + <tbody> + <tr> + <td class="header">屬性</td> + <td class="header">型別</td> + <td class="header">描述</td> + </tr> + <tr> + <td><code>font</code> {{ gecko_minversion_inline("1.9.1") }}</td> + <td><code><a href="/En/DOM/DOMString" title="en/DOM/DOMString">DOMString</a></code></td> + <td> + <p>當前的文字樣式被用在繪製文字。該字串使用和 <a href="/en/CSS/font" title="en/CSS/font">CSS font</a>(樣式表字型)相同的語法。要改變繪製文字的樣式,只要簡單的改變它的屬性值即可,就像下面展示的,預設的字型是10px(像素) sans-serif(字型名稱)</p> + + <p>例如:</p> + + <pre class="eval"> +ctx.font = "20pt Arial";</pre> + </td> + </tr> + <tr> + <td><code>mozTextStyle</code> {{ deprecated_inline() }}</td> + <td><code><a href="/En/DOM/DOMString" title="en/DOM/DOMString">DOMString</a></code></td> + <td> + <p>由上面的Html5<code>字型</code> 屬性取代</p> + </td> + </tr> + <tr> + <td><code>textAlign</code> {{ gecko_minversion_inline("1.9.1") }}</td> + <td><code><a href="/En/DOM/DOMString" title="en/DOM/DOMString">DOMString</a></code></td> + <td> + <p>當前繪製文字所使用的文字對齊方式。 可使用的值:</p> + + <dl> + <dt>left</dt> + <dd>文字靠左對齊。</dd> + <dt>right</dt> + <dd>文字靠右對齊。</dd> + <dt>center</dt> + <dd>文字置中對齊。</dd> + <dt>start</dt> + <dd>文字依照行首對齊 (書寫習慣由左到右的地區就靠左對齊,書寫習慣由右到左的就靠右對齊。).</dd> + <dt>end</dt> + <dd>文字依照行尾對齊(書寫習慣由左到右的地區就靠右對齊,書寫習責由右到左的地區就靠左對齊。)</dd> + </dl> + + <p>預設的值是 <code>start</code>.</p> + </td> + </tr> + <tr> + <td><code>textBaseline</code> {{ gecko_minversion_inline("1.9.1") }}</td> + <td><code><a href="/En/DOM/DOMString" title="en/DOM/DOMString">DOMString</a></code></td> + <td> + <p>當前繪製文字的基線位置 可使用的值:</p> + + <dl> + <dt>top</dt> + <dd>基線在字元區塊的頂部(圖中top of the squre位置)。</dd> + <dt>hanging(懸掛)</dt> + <dd>文字基線在拼音文字頂部的位置(圖中hanging baseline) <em>當前仍未支援;會顯示 <strong>alphabetic</strong>代替。</em></dd> + <dt>middle</dt> + <dd>文字基線在字元區塊的中間。</dd> + <dt>alphabetic(拼音文字)</dt> + <dd>這是一般拼音文字底線的位置。</dd> + <dt>ideographic(表意文字)</dt> + <dd>文字在表意文字(如漢字)底部的位置 <em>當前仍未支援;會顯示<strong>alphabetic</strong>代替。</em></dd> + <dt>bottom</dt> + <dd>基線在拼音文字下伸部的位置 這與ideographic的基線位置不同,因為表意文字沒有下伸部</dd> + </dl> + + <p>預設使用 <code>alphabetic</code>.</p> + </td> + </tr> + </tbody> +</table> + +<p>下圖展示了textBaseline屬性所支援的各種基線,感謝 <a class="external" href="http://www.whatwg.org/" title="http://www.whatwg.org/">WHATWG</a>.</p> + +<p><img alt="top of em squre(字元區塊頂部)大致在字型中所有字母的最頂部位置,hanging basline(懸掛基線)則是在一些特殊(較小的,像是“आ”)字母頂部,middle則是在top of em squre(字元區塊頂部和bottom of em squre(字元區塊底部)的中間,alphabetic(拼音文字)的基線位置則是在一般拼音字母如Á,ÿ,f,Ω的底線位置。ideographic(表意文字)的基線在字元的底部位置,bottom of em squre(字元區塊底部)則大致是字型中所有字母的最底部位置。而top and bottom of the bounding box(上下的區域範圍線)則比這些基線都來得更遠,基於字母的高度可能超過字元區塊頂部和底部的範圍。" src="http://www.whatwg.org/specs/web-apps/current-work/images/baselines.png"></p> + +<h2 id="Methods" name="Methods">方法</h2> + +<h3 id="fillText()">fillText()</h3> + +<p>繪製文字使用<code>font</code>屬性指定的文字樣式,對齊則使用<code>textAlign</code>屬性,而指定基線則使用<code>textBaseline</code>. 填充文字當前使用<code>fillStyle</code>,而<code>strokeStyle</code>則被忽略</p> + +<div class="note"><strong>注意:</strong>這個方法在 Gecko 1.9.1 (Firefox 3.5)時引進,且是HTML 5標準的一部分.</div> + +<pre>void fillText( + in DOMString textToDraw, + in float x, + in float y, + [optional] in float maxWidth +); +</pre> + +<h5 id="參數">參數</h5> + +<dl> + <dt><code>textToDraw</code></dt> + <dd>將文字繪製到文本中。</dd> + <dt><code>x</code></dt> + <dd>繪製位置的x座標。</dd> + <dt><code>y</code></dt> + <dd>繪製位置的y座標。</dd> + <dt><code>maxWidth</code></dt> + <dd>最大寬度,可選用的;繪製字串最大長度 如果指定此參數,當字串被計算出比這個值更寬,它會自動選擇水平方向更窄的字型(如果有可用的字型或是有可讀的字型可以嵌入當前字型之中),或者縮小字型。</dd> +</dl> + +<h5 id="範例">範例</h5> + +<pre class="brush: js">ctx.fillText("Sample String", 10, 50); +</pre> + +<h3 id="mozMeasureText.28.29" name="mozMeasureText.28.29">measureText()</h3> + +<p>測量文字。返回一個物件包含了寬度,像素值,所指定的文字會以當前的文字樣式繪製。</p> + +<div class="note"><strong>注意:</strong> 這個方法在 Gecko 1.9.1 (Firefox 3.5) 引進,且是HTML 5標準的一部分。</div> + +<pre class="eval">nsIDOMTextMetrics measureText( + in DOMString textToMeasure +); +</pre> + +<h6 id="Parameters_2" name="Parameters_2">參數</h6> + +<dl> + <dt><code>textToMeasure</code></dt> + <dd>該字串的像素值。</dd> +</dl> + +<h6 id="Return_value" name="Return_value">返回值</h6> + +<p><a class="internal" href="/nsIDOMTextMetrics" title="nsIDOMTextMetrics"><code>nsIDOMTextMetrics</code></a>物件的<code>width</code>屬性在繪製時會將數字設定給CSS 的像素值寬度。</p> + +<h3 id="mozDrawText.28.29" name="mozDrawText.28.29">mozDrawText()</h3> + +<p>{{ deprecated_header() }}</p> + +<p>繪製文字使用由<code>mozTextStyle</code>屬性的文字樣式。文本當前的填充顏色被用來當做文字顏色。</p> + +<div class="note"><strong>注意:</strong>這個方法已經不被建議使用,請使用正式的HTML 5方法 <code>fillText()</code> and <code>strokeText()</code>.</div> + +<pre class="eval">void mozDrawText( + in DOMString textToDraw +); +</pre> + +<h6 id="Parameters" name="Parameters">參數</h6> + +<dl> + <dt><code>textToDraw</code></dt> + <dd>將文字繪製到文本。</dd> +</dl> + +<h6 id="Example" name="Example">範例</h6> + +<pre class="brush: js">ctx.translate(10, 50); +ctx.fillStyle = "Red"; +ctx.mozDrawText("Sample String"); +</pre> + +<p>這個範例將文字“Sample String”繪製到畫布(canvas)上。</p> + +<h3 id="mozMeasureText.28.29" name="mozMeasureText.28.29">mozMeasureText()</h3> + +<p>{{ deprecated_header() }}</p> + +<p>返回寬度,像素值,指定文字</p> + +<div class="note"><strong>注意:</strong>這個方法已經已宣告棄用,請使用正式的HTML 5方法<code>measureText()</code>.</div> + +<pre class="eval">float mozMeasureText( + in DOMString textToMeasure +); +</pre> + +<h6 id="Parameters_2" name="Parameters_2">參數</h6> + +<dl> + <dt><code>textToMeasure</code></dt> + <dd>字串的寬度像素值</dd> +</dl> + +<h6 id="Return_value" name="Return_value">返回值</h6> + +<p>文字的寬度像素值</p> + +<h6 id="Example_2" name="Example_2">範例</h6> + +<pre class="brush: js">var text = "Sample String"; +var width = ctx.canvas.width; +var len = ctx.mozMeasureText(text); +ctx.translate((width - len)/2, 0); +ctx.mozDrawText(text); +</pre> + +<p>這個範例測量了字串的寬度,接著使用這個資訊將它畫在畫布(canvas)的水平中心。</p> + +<h3 id="mozPathText.28.29" name="mozPathText.28.29">mozPathText()</h3> + +<p>給文字路徑加上外框線,如果你想要的話,它允許你替文字加上框線代替填充它。</p> + +<pre class="eval">void mozPathText( + in DOMString textToPath +); +</pre> + +<h6 id="Parameters_3" name="Parameters_3">參數</h6> + +<dl> + <dt><code>textToPath</code></dt> + <dd>為當前的文字路徑加上框線</dd> +</dl> + +<h6 id="Example_3" name="Example_3">Example</h6> + +<pre class="brush: js">ctx.fillStyle = "green"; +ctx.strokeStyle = "black"; +ctx.mozPathText("Sample String"); +ctx.fill() +ctx.stroke() +</pre> + +<p>這個範例繪出文字“Sample String”,填充顏色是綠色,外框顏色是黑色。</p> + +<h3 id="mozTextAlongPath.28.29" name="mozTextAlongPath.28.29">mozTextAlongPath()</h3> + +<p>Adds (or draws) the specified text along the current path.</p> + +<pre class="eval">void mozTextAlongPath( + in DOMString textToDraw, + in boolean stroke +); +</pre> + +<h6 id="Parameters_4" name="Parameters_4">參數</h6> + +<dl> + <dt><code>textToDraw</code></dt> + <dd>沿著指定路徑繪出文字</dd> + <dt><code>stroke</code></dt> + <dd>如果參數是 <code>true</code>(真值),文字會沿著指定路徑繪製。如果 <code>false</code>(假值),這個文字則會加入到路徑之中,再沿著當前路徑繪製。</dd> +</dl> + +<h6 id="Remarks" name="Remarks">備註</h6> + +<p>字體不會沿著路徑曲線縮放或變形,反而在彎曲路徑下,字體每次計算都會當成是直線在處理。這可以用來建立一些特殊的效果。</p> + +<h3 id="strokeText()">strokeText()</h3> + +<p>繪製文字使用<code>font</code>屬性指定的文字樣式,對齊則使用<code>textAlign</code>屬性,而指定基線則使用<code>textBaseline</code>. 當前使用<code>strokeStyle</code>來建立文字外框。</p> + +<div class="note"><strong>注意:</strong> 這個方法在 Gecko 1.9.1 (Firefox 3.5)時引進,且是HTML 5標準的一部分。</div> + +<pre>void strokeText( + in DOMString textToDraw, + in float x, + in float y, + [optional] in float maxWidth +); +</pre> + +<h5 id="參數_2">參數</h5> + +<dl> + <dt><code>textToDraw</code></dt> + <dd>將文字繪製到文本中。</dd> + <dt><code>x</code></dt> + <dd>繪製位置的x座標。</dd> + <dt><code>y</code></dt> + <dd>繪製位置的y座標</dd> + <dt><code>maxWidth</code></dt> + <dd>最大寬度,可選用的;繪製字串最大長度 如果指定此參數,當字串被計算出比這個值更寬,它會自動選擇水平方向更窄的字型(如果有可用的字型或是有可讀的字型可以嵌入當前字型之中),或者縮小字型。</dd> +</dl> + +<h5 id="範例_2">範例</h5> + +<pre class="brush: js">ctx.strokeText("Sample String", 10, 50); +</pre> + +<h2 id="Remarks_2" name="Remarks_2">備註</h2> + +<ul> + <li>請見 <a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/#text" title="http://www.whatwg.org/specs/web-apps/current-work/#text">WHATWG specification</a> 關於HTML 5 canvas text的說明。</li> + <li>你不需要特別的文本來使用這些功能;2D的文本就可以執行得很好。</li> + <li>所有的繪圖都使用即時變化來完成。</li> +</ul> + +<h2 id="Additional_examples" name="Additional_examples">其它範例</h2> + +<ul> + <li><a class="link-https" href="https://bugzilla.mozilla.org/attachment.cgi?id=273497">繞著路徑繪製外框文字</a></li> + <li><a class="link-https" href="https://bugzilla.mozilla.org/attachment.cgi?id=273498">沿著路徑繪製</a></li> + <li><a class="link-https" href="https://bugzilla.mozilla.org/attachment.cgi?id=273499">動畫文字繞圓移動</a>.</li> +</ul> + +<h1 id="瀏覽器兼容性">瀏覽器兼容性</h1> + +<p>{{ CompatibilityTable() }}</p> + +<div id="compat-desktop"> +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Internet Explorer</th> + <th>Opera</th> + <th>Safari</th> + </tr> + <tr> + <td>Basic support</td> + <td>1+</td> + <td>3.5+</td> + <td>9.0</td> + <td>10.5</td> + <td>4.0</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Firefox Mobile (Gecko)</th> + <th>IE Mobile</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + </tr> + <tr> + <td>Basic support</td> + <td>2.1</td> + <td>{{ CompatUnknown() }}</td> + <td>9.0</td> + <td>11.0</td> + <td>3.2</td> + </tr> + </tbody> +</table> +</div> +</div> + +<p> </p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/index.html b/files/zh-tw/web/api/canvas_api/tutorial/index.html new file mode 100644 index 0000000000..15280a881d --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/index.html @@ -0,0 +1,69 @@ +--- +title: Canvas 教學文件 +slug: Web/API/Canvas_API/Tutorial +translation_of: Web/API/Canvas_API/Tutorial +--- +<div>{{CanvasSidebar}}</div> + +<p><a href="/en-US/docs/HTML/Canvas" title="HTML/Canvas"><img alt="" src="https://mdn.mozillademos.org/files/257/Canvas_tut_examples.jpg" style="float: right; height: 450px; width: 200px;"></a></p> + +<div class="summary"> +<p><a href="/en-US/docs/HTML/Canvas" title="HTML/Canvas"><strong><code><canvas></code></strong></a> 是一個 <a href="/en-US/docs/HTML" title="HTML">HTML</a> 元素,我們可以利用程式腳本在這個元素上繪圖(通常是用 <a href="/en-US/docs/JavaScript" title="JavaScript">JavaScript</a>)。除了繪圖,我們還可以合成圖片或做一些簡單(或是<a href="/en-US/docs/HTML/Canvas/A_Basic_RayCaster" title="A_Basic_RayCaster">不那麼簡單</a>)的動畫。右方的影像便是一些運用 <canvas> 的例子,接下來我們將會在教學文件中一一說明<span style="line-height: inherit;">。</span></p> +</div> + +<p><span class="seoSummary">本教學從基礎知識開始,描述如何利用 <canvas> 進行 2D 繪圖。教學中的範例會讓各位清楚瞭解 <canvas> 該如何運用,另外也會提供程式碼範例,讓大家嘗試製作自己的內容。</span></p> + +<p><code><canvas></code> 最早是由 Apple 為 Mac OS X Dashboard 所提出,之後 Safari 和 Google Chrome 也都採用。<a href="/en-US/docs/Gecko" title="Gecko">Gecko</a> 1.8 作基礎的瀏覽器,如 Firefox 1.5 也都提供了支援。<code><canvas></code> 元素是 <a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/">WhatWG Web applications 1.0</a>(也就是 HTML5)規範的一部分,目前所有主流的瀏覽器都已支援。</p> + +<h2 id="Before_you_start" name="Before_you_start">在開始之前</h2> + +<p><code><canvas></code> 並不困難,但你需要了解基本的 <a href="/en-US/docs/HTML" title="HTML">HTML</a> 與 <a href="/en-US/docs/JavaScript" title="JavaScript">JavaScript</a>。部分舊版瀏覽器不支援<code> <canvas>,不過基本上現今所有主流的瀏覽器都有支援。</code>預設的畫布大小是 300px * 150px(寬 * 高)。但你也可以透過 HTML 寬、高屬性({{Glossary("attribute")}})自訂。為了在畫布上作畫,我們使用了一個 JavaScript context 物件來即時繪製圖形。</p> + +<h2 id="In_this_tutorial" name="In_this_tutorial">教學文件</h2> + +<ul> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Basic_usage" title="Canvas_tutorial/Basic_usage">基本用途</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes" title="Canvas_tutorial/Drawing_shapes">繪製圖形</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors" title="Canvas_tutorial/Applying_styles_and_colors">套用樣式與顏色</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text">繪製文字</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Using_images" title="Canvas_tutorial/Using_images">使用影像</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Transformations" title="Canvas_tutorial/Transformations">變形效果</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Compositing" title="Canvas_tutorial/Compositing">合成效果</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Basic_animations" title="Canvas_tutorial/Basic_animations">基礎動畫</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Advanced_animations">進階動畫</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas">像素控制</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility">Hit regions and accessibility</a></li> + <li><a href="/zh-TW/docs/Web/Guide/HTML/Canvas_tutorial/Optimizing_canvas" title="Canvas_tutorial/Optimizing_canvas">最佳化 canvas</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Finale">Finale</a></li> +</ul> + +<h2 id="See_also" name="See_also">參見</h2> + +<ul> + <li><a href="/en-US/docs/HTML/Canvas" title="HTML/Canvas">Canvas topic page</a></li> + <li><a class="external external-icon" href="http://visitmix.com/labs/ai2canvas/" title="http://visitmix.com/labs/ai2canvas/">Adobe Illustrator to Canvas plug-in</a></li> + <li><a class="external external-icon" href="http://www.html5canvastutorials.com/" title="http://www.html5canvastutorials.com/">HTML5CanvasTutorials</a></li> + <li><a class="external external-icon" href="http://creativejs.com/2011/08/31-days-of-canvas-tutorials/" title="http://creativejs.com/2011/08/31-days-of-canvas-tutorials/">31 days of canvas tutorials</a></li> + <li><a href="/en-US/docs/HTML/Canvas/Drawing_Graphics_with_Canvas" title="Drawing_Graphics_with_Canvas">Drawing Graphics with Canvas</a></li> + <li><a href="/en-US/docs/tag/Canvas_examples" title="tag/Canvas_examples">Canvas examples</a></li> + <li><a class="external" href="http://html5tutorial.com" title="http://html5tutorial.com">HTML5 Tutorial</a></li> + <li><a href="/en-US/docs/Drawing_text_using_a_canvas" title="Drawing_text_using_a_canvas">Drawing Text Using a Canvas</a></li> + <li><a class="external" href="http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/HTML-canvas-guide/AddingText/AddingText.html#//apple_ref/doc/uid/TP40010542-CH6-SW4" title="Adding Text to Canvas">Adding Text to Canvas</a></li> + <li><a class="external" href="http://www.canvasdemos.com/" title="Canvas Demos">Canvas Demos - Games, applications, tools and tutorials</a></li> + <li><a class="external" href="http://canvimation.github.com/" title="http://canvimation.github.com/">Canvas Drawing and Animation Application</a></li> + <li><a class="external" href="http://billmill.org/static/canvastutorial/" title="http://billmill.org/static/canvastutorial/">interactive canvas tutorial</a></li> + <li><a class="external" href="http://blog.nihilogic.dk/2009/02/html5-canvas-cheat-sheet.html" title="http://blog.nihilogic.dk/2009/02/html5-canvas-cheat-sheet.html">Canvas Cheat Sheet with all attributes and methods</a></li> + <li><a class="external" href="http://visitmix.com/labs/ai2canvas/" title="http://visitmix.com/labs/ai2canvas/">Adobe Illustrator to Canvas plug-in</a></li> + <li><a class="external" href="http://www.html5canvastutorials.com/" title="http://www.html5canvastutorials.com/">HTML5CanvasTutorials</a></li> + <li><a class="external" href="http://html5tutorial.com/how-to-draw-n-grade-bezier-curve-with-canvas-api" title="http://html5tutorial.com/how-to-draw-n-grade-bezier-curve-with-canvas-api">How to draw N grade Bézier curves with the Canvas API</a></li> + <li><a class="external" href="http://creativejs.com/2011/08/31-days-of-canvas-tutorials/" title="http://creativejs.com/2011/08/31-days-of-canvas-tutorials/">31 days of canvas tutorials</a></li> + <li><a href="http://www.w3.org/TR/2dcontext/" title="http://www.w3.org/TR/2dcontext/">W3C Standard</a></li> +</ul> + +<h2 id="致歉各位貢獻者">致歉各位貢獻者</h2> + +<p>由於 2013/6/17 那一週的不幸技術錯誤,所有有關本教學的歷史紀錄,包括過去所有貢獻者的紀錄都遺失了,我們深感抱歉,希望各位可以原諒這一次不幸的意外。</p> + +<div>{{ Next("Web/Guide/HTML/Canvas_tutorial/Basic_usage") }}</div> + +<div> </div> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/optimizing_canvas/index.html b/files/zh-tw/web/api/canvas_api/tutorial/optimizing_canvas/index.html new file mode 100644 index 0000000000..7fb8b35666 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/optimizing_canvas/index.html @@ -0,0 +1,26 @@ +--- +title: 最佳化canvas +slug: Web/API/Canvas_API/Tutorial/Optimizing_canvas +translation_of: Web/API/Canvas_API/Tutorial/Optimizing_canvas +--- +<p>{{HTMLElement("canvas")}}在網頁2D繪圖上被大量運用於遊戲和複雜視覺化效果上。隨著繪圖複雜度越來越高,效能問題也會逐一浮現,所以最後我們在這邊列出一些最佳化畫布的方法,避免一些效能問題:</p> +<ul> + <li> + 在畫面之外的畫布上先行渲染相似或重複的物件 + </li> + <li>批次性執行繪圖,例如一次畫數個線條而非分開一次一次畫</li> + <li> + 使用整數位座標值,盡量避免小數點值 + </li> + <li>避免不必要的畫布狀態改變</li> + <li> + 只渲染不同處,不要全部重新渲染 + </li> + <li>對於複雜需求可以利用多個畫布分層構成</li> + <li> + 盡量不要用shadowBlur屬性 + </li> + <li>使用{{domxref("window.requestAnimationFrame()")}}</li> + <li>用<a href="http://jsperf.com" title="http://jsperf.com">JSPerf</a>測試效能</li> +</ul> +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Basic_animations")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html b/files/zh-tw/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html new file mode 100644 index 0000000000..398ef70484 --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html @@ -0,0 +1,265 @@ +--- +title: Pixel manipulation with canvas +slug: Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas +translation_of: Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Advanced_animations", "Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility")}}</div> + +<div class="summary"> +<p>直到目前為止,我們還沒真正了解 pixels 在 canvas上的運用。使用<code>ImageData</code>物件,可直接對pixel 裡的陣列資料<strong>讀(read)</strong>和<strong>寫(write)</strong>。在接下的內容中,也可了解到如何使影像平滑化(反鋸齒)及如何將影像保存在canvas之中。</p> +</div> + +<h2 id="ImageData物件"><code>ImageData</code>物件</h2> + +<p>{{domxref("ImageData")}} 物件代表canvas區中最基礎的像素。</p> + +<p>包含它只可讀的屬性:</p> + +<dl> + <dt><code>width</code></dt> + <dd>影像中的寬度,以pixels為單位</dd> + <dt><code>height</code></dt> + <dd>影像中的高度,以pixels為單位</dd> + <dt><code>data</code></dt> + <dd>{{jsxref("Uint8ClampedArray")}} 代表一維陣列包含RGBA 格式。整數值介於0到255之間(包含255)。</dd> +</dl> + +<p><code>data</code> 屬性返回一個{{jsxref("Uint8ClampedArray")}},它可被當作為pixel的初始資料。每個pixel用4個1byte值做代表分別為<strong>紅</strong>,<strong>綠</strong>,<strong>藍</strong>,<strong>透明值</strong>(也就是<strong>RGBA</strong>格式)。每個顏色組成皆是介於整數值介於0到255之間。而每個組成在一個陣列中被分配為一個連續的索引。從左上角 pixel 的紅色組成中的陣列由<strong>索引 0</strong> 為始。Pixels 執行順序為從左到右,再由上到下,直到整個陣列。</p> + +<p>{{jsxref("Uint8ClampedArray")}} 包含<code>height</code> × <code>width</code>× 4 bytes的資料,同索引值從0到 (<code>height</code>×<code>width</code>×4)-1</p> + +<p>例如,讀取影像的藍色組成的值。從pixel 的第200欄、第50行,你可以照著下面的步驟:</p> + +<pre class="brush: js">blueComponent = imageData.data[((50 * (imageData.width * 4)) + (200 * 4)) + 2];</pre> + +<p>使用<code>Uint8ClampedArray.length</code>屬性來讀取影像pixel的陣列大小</p> + +<pre class="brush: js">var numBytes = imageData.data.length; +</pre> + +<h2 id="創造一個_ImageData物件">創造一個 <code>ImageData</code>物件</h2> + +<p>可以使用{{domxref("CanvasRenderingContext2D.createImageData", "createImageData()")}}方法創造一個全新空白的<code>ImageData</code> 物件。</p> + +<p>這裡有兩種<code>createImageData()</code>的方法:</p> + +<pre class="brush: js">var myImageData = ctx.createImageData(width, height);</pre> + +<p>這個方法是有規定大小尺寸.所有pixels預設是透明的黑色。</p> + +<p>下面的方法一樣是由<code>anotherImageData</code>參考尺寸大小,由<code>ImageData</code> 物件創造一個與新的一樣的大小。這些新的物件的pixel皆預設為透明的黑色。</p> + +<pre class="brush: js">var myImageData = ctx.createImageData(anotherImageData);</pre> + +<h2 id="得到pixel資料的內容">得到pixel資料的內容</h2> + +<p>可以使用<code>getImageData()</code>這個方法,去取得canvas內容中<code>ImageData</code> 物件的資料含pixel 數據(data) </p> + +<pre class="brush: js">var myImageData = ctx.getImageData(left, top, width, height);</pre> + +<p>這個方法會返回<code>ImageData</code>物件,它代表著在這canvas區域之中pixel 的數據(data) 。從各角落的點代表著 (<code>left</code>,<code>top</code>), (<code>left+width</code>, <code>top</code>), (<code>left</code>, <code>top+height</code>), and (<code>left+width</code>, <code>top+height</code>)。這些作標被設定為canvas 的空間座標單位。</p> + +<div class="note"> +<p><strong>注釋</strong>: 在<code>ImageData</code> 物件中,任何超出canvas外的pixels皆會返回透明的黑色的形式。</p> +</div> + +<p>這個方法也被展示在<a href="/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas">使用canvas操作影像</a>之中。</p> + +<h3 id="調色盤">調色盤</h3> + +<p>這個範例使用<a href="/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData">getImageData() </a>方法去顯示在鼠標下的顏色。</p> + +<p>首先,需要一個正確的滑鼠點<code>layerX</code><em></em>和 <code>layerY</code>。在從<a href="/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData">getImageData()</a> 提供pixel 陣列中(array)該點的pixel 數據(data) 。最後,使用陣列數據(array data)在<code><div></code>中設置背景色和文字去顯示該色。</p> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="300" height="227" style="float:left"></canvas> +<div id="color" style="width:200px;height:50px;float:left"></div> +</pre> +</div> + +<pre class="brush: js;">var img = new Image(); +img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; +var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +img.onload = function() { + ctx.drawImage(img, 0, 0); + img.style.display = 'none'; +}; +var color = document.getElementById('color'); +function pick(event) { + var x = event.layerX; + var y = event.layerY; + var pixel = ctx.getImageData(x, y, 1, 1); + var data = pixel.data; + var rgba = 'rgba(' + data[0] + ', ' + data[1] + + ', ' + data[2] + ', ' + (data[3] / 255) + ')'; + color.style.background = rgba; + color.textContent = rgba; +} +canvas.addEventListener('mousemove', pick); +</pre> + +<p>{{ EmbedLiveSample('A_color_picker', 610, 240) }}</p> + +<h2 id="在內容中寫入pixel_資料">在內容中寫入pixel 資料</h2> + +<p>可以使用<a href="/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData">putImageData() </a>方法將自訂pixel 數據(data) 放入內容中:</p> + +<pre class="brush: js">ctx.putImageData(myImageData, dx, dy); +</pre> + +<p><code>dx</code> 和 <code>dy</code>參數表示填入你所希望的座標,將它代入內容中左上角的pixel 數據(data)。</p> + +<p>For example, to paint the entire image represented by <code>myImageData</code> to the top left corner of the context, you can simply do the following:</p> + +<pre class="brush: js">ctx.putImageData(myImageData, 0, 0); +</pre> + +<h3 id="灰階和負片效果">灰階和負片效果</h3> + +<p>In this example we iterate over all pixels to change their values, then we put the modified pixel array back to the canvas using <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData">putImageData()</a>. The invert function simply subtracts each color from the max value 255. The grayscale function simply uses the average of red, green and blue. You can also use a weighted average, given by the formula <code>x = 0.299r + 0.587g + 0.114b</code>, for example. See <a href="http://en.wikipedia.org/wiki/Grayscale">Grayscale</a> on Wikipedia for more information.</p> + +<div class="hidden"> +<pre class="brush: html;"><canvas id="canvas" width="300" height="227"></canvas> +<div> + <input id="grayscalebtn" value="Grayscale" type="button"> + <input id="invertbtn" value="Invert" type="button"> +</div> +</pre> +</div> + +<pre class="brush: js">var img = new Image(); +img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; +img.onload = function() { + draw(this); +}; + +function draw(img) { + var canvas = document.getElementById('canvas'); + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + img.style.display = 'none'; + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var data = imageData.data; + + var invert = function() { + for (var i = 0; i < data.length; i += 4) { + data[i] = 255 - data[i]; // red + data[i + 1] = 255 - data[i + 1]; // green + data[i + 2] = 255 - data[i + 2]; // blue + } + ctx.putImageData(imageData, 0, 0); + }; + + var grayscale = function() { + for (var i = 0; i < data.length; i += 4) { + var avg = (data[i] + data[i + 1] + data[i + 2]) / 3; + data[i] = avg; // red + data[i + 1] = avg; // green + data[i + 2] = avg; // blue + } + ctx.putImageData(imageData, 0, 0); + }; + + var invertbtn = document.getElementById('invertbtn'); + invertbtn.addEventListener('click', invert); + var grayscalebtn = document.getElementById('grayscalebtn'); + grayscalebtn.addEventListener('click', grayscale); +} +</pre> + +<p>{{ EmbedLiveSample('Grayscaling_and_inverting_colors', 330, 270) }}</p> + +<h2 id="放大和平滑化(反鋸齒)">放大和平滑化(反鋸齒)</h2> + +<p>With the help of the {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}} method, a second canvas and the {{domxref("CanvasRenderingContext2D.imageSmoothingEnabled", "imageSmoothingEnabled")}} property, we are able to zoom into our picture and see the details.</p> + +<p>We get the position of the mouse and crop an image of 5 pixels left and above to 5 pixels right and below. Then we copy that one over to another canvas and resize the image to the size we want it to. In the zoom canvas we resize a 10×10 pixel crop of the original canvas to 200×200.</p> + +<pre class="brush: js">zoomctx.drawImage(canvas, + Math.abs(x - 5), Math.abs(y - 5), + 10, 10, 0, 0, 200, 200);</pre> + +<p>Because anti-aliasing is enabled by default, we might want to disable the smoothing to see clear pixels. You can toggle the checkbox to see the effect of the <code>imageSmoothingEnabled</code> property (which needs prefixes for different browsers).</p> + +<h6 class="hidden" id="Zoom_example">Zoom example</h6> + +<div class="hidden"> +<pre class="brush: html;"><canvas id="canvas" width="300" height="227"></canvas> +<canvas id="zoom" width="300" height="227"></canvas> +<div> +<label for="smoothbtn"> + <input type="checkbox" name="smoothbtn" checked="checked" id="smoothbtn"> + Enable image smoothing +</label> +</div> +</pre> +</div> + +<pre class="brush: js">var img = new Image(); +img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; +img.onload = function() { + draw(this); +}; + +function draw(img) { + var canvas = document.getElementById('canvas'); + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + img.style.display = 'none'; + var zoomctx = document.getElementById('zoom').getContext('2d'); + + var smoothbtn = document.getElementById('smoothbtn'); + var toggleSmoothing = function(event) { + zoomctx.imageSmoothingEnabled = this.checked; + zoomctx.mozImageSmoothingEnabled = this.checked; + zoomctx.webkitImageSmoothingEnabled = this.checked; + zoomctx.msImageSmoothingEnabled = this.checked; + }; + smoothbtn.addEventListener('change', toggleSmoothing); + + var zoom = function(event) { + var x = event.layerX; + var y = event.layerY; + zoomctx.drawImage(canvas, + Math.abs(x - 5), + Math.abs(y - 5), + 10, 10, + 0, 0, + 200, 200); + }; + + canvas.addEventListener('mousemove', zoom); +}</pre> + +<p>{{ EmbedLiveSample('Zoom_example', 620, 490) }}</p> + +<h2 id="儲存圖片">儲存圖片</h2> + +<p>The {{domxref("HTMLCanvasElement")}} provides a <code>toDataURL()</code> method, which is useful when saving images. It returns a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs">data URI</a> containing a representation of the image in the format specified by the <code>type</code> parameter (defaults to <a class="external external-icon" href="https://en.wikipedia.org/wiki/Portable_Network_Graphics">PNG</a>). The returned image is in a resolution of 96 dpi.</p> + +<dl> + <dt>{{domxref("HTMLCanvasElement.toDataURL", "canvas.toDataURL('image/png')")}}</dt> + <dd>Default setting. Creates a PNG image.</dd> + <dt>{{domxref("HTMLCanvasElement.toDataURL", "canvas.toDataURL('image/jpeg', quality)")}}</dt> + <dd>Creates a JPG image. Optionally, you can provide a quality in the range from 0 to 1, with one being the best quality and with 0 almost not recognizable but small in file size.</dd> +</dl> + +<p>Once you have generated a data URI from you canvas, you are able to use it as the source of any {{HTMLElement("image")}} or put it into a hyper link with a <a href="/en-US/docs/Web/HTML/Element/a#attr-download">download attribute</a> to save it to disc, for example.</p> + +<p>You can also create a {{domxref("Blob")}} from the canvas.</p> + +<dl> + <dt>{{domxref("HTMLCanvasElement.toBlob", "canvas.toBlob(callback, type, encoderOptions)")}}</dt> + <dd>Creates a <code>Blob</code> object representing the image contained in the canvas.</dd> +</dl> + +<h2 id="延伸閱讀">延伸閱讀</h2> + +<ul> + <li>{{domxref("ImageData")}}</li> + <li><a href="/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas">Manipulating video using canvas</a></li> + <li><a href="https://codepo8.github.io/canvas-images-and-pixels/">Canvas, images and pixels – by Christian Heilmann</a></li> +</ul> + +<p>{{PreviousNext("Web/API/Canvas_API/Tutorial/Advanced_animations", "Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/transformations/index.html b/files/zh-tw/web/api/canvas_api/tutorial/transformations/index.html new file mode 100644 index 0000000000..b25a2248dd --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/transformations/index.html @@ -0,0 +1,336 @@ +--- +title: 變形效果 +slug: Web/API/Canvas_API/Tutorial/Transformations +translation_of: Web/API/Canvas_API/Tutorial/Transformations +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Using_images", "Web/API/Canvas_API/Tutorial/Compositing")}}</div> + +<h2 id="Saving_and_restoring_state" name="Saving_and_restoring_state">畫布狀態儲存與復原</h2> + +<p>在使用變形效果等複雜繪圖方法之前,有兩個不可或缺的方法(method)必須要先了解一下:</p> + +<dl> + <dt><code>save()</code></dt> + <dd>儲存現階段畫布完整狀態。</dd> + <dt><code>restore()</code></dt> + <dd>復原最近一次儲存的畫布狀態。</dd> +</dl> + +<p>每一次呼叫save(),畫布狀態便會存進一個堆疊(stack)之中。畫布狀態包含了:</p> + +<ul> + <li>曾經套用過的變形效果,如translate, rotate和scale(稍後說明)。</li> + <li><code>strokeStyle</code>, <code>fillStyle</code>, <code>globalAlpha</code>, <code>lineWidth</code>, <code>lineCap</code>, <code>lineJoin</code>, <code>miterLimit</code>, <code>shadowOffsetX</code>, <code>shadowOffsetY</code>, <code>shadowBlur</code>, <code>shadowColor</code>, <code>globalCompositeOperation</code> 屬性的屬性值</li> + <li>目前截圖路徑(稍後說明)。</li> +</ul> + +<p>我們可以呼叫save()的次數不限,而每一次呼叫restore(),最近一次儲存的畫布狀態便會從堆疊中被取出,然後還原畫布到此畫布狀態。</p> + +<h3 id="A_save_and_restore_canvas_state_example" name="A_save_and_restore_canvas_state_example">畫布狀態儲存與復原範例</h3> + +<p>本例會畫一連串矩形圖案來說明畫布狀態堆疊是如何運作。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + ctx.fillRect(0,0,150,150); // Draw a rectangle with default settings + ctx.save(); // Save the default state + + ctx.fillStyle = '#09F' // Make changes to the settings + ctx.fillRect(15,15,120,120); // Draw a rectangle with new settings + + ctx.save(); // Save the current state + ctx.fillStyle = '#FFF' // Make changes to the settings + ctx.globalAlpha = 0.5; + ctx.fillRect(30,30,90,90); // Draw a rectangle with new settings + + ctx.restore(); // Restore previous state + ctx.fillRect(45,45,60,60); // Draw a rectangle with restored settings + + ctx.restore(); // Restore original state + ctx.fillRect(60,60,30,30); // Draw a rectangle with restored settings +}</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p> </p> + +<p>* 預設用黑色填滿這個矩形</p> + +<p>* 每種狀態可以看成是一層層堆疊的步驟紀錄</p> + +<p>第一步:畫出黑色矩形 接著把第一個狀態儲存下來(用黑色填滿)</p> + +<p>第二步:畫出藍色矩形 接著把第二個狀態儲存下來(用藍色填滿)</p> + +<p>第三步:畫出半透明矩形</p> + +<p>第四步:再畫出矩形 這時候我們取用最新儲存過的藍色(第二狀態)</p> + +<p>第五步:再畫一個矩形 我們再取出更早之前儲存的黑色(第一狀態)</p> + +<p>{{EmbedLiveSample("A_save_and_restore_canvas_state_example", "180", "180", "https://mdn.mozillademos.org/files/249/Canvas_savestate.png")}}</p> + +<h2 id="Translating" name="Translating">移動畫布</h2> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/234/Canvas_grid_translate.png" style="float: right;">第一個變形效果方法是translate()。translate()是用來移動畫布,如右圖,原先畫布的原點在網格(0, 0)位置,我們可以移動畫布,使其原點移到(x, y)位置。</p> + +<dl> + <dt><code>translate(<em>x</em>, <em>y</em>)</code></dt> + <dd>移動網格上的畫布,其中x代表水平距離、y代表垂直距離。</dd> +</dl> + +<p>最好在做任何變形效果前先儲存一下畫布狀態,如此當我們需要復原先前的狀態時,只需要呼叫一下restore()即可,而且有一種情況是當我們在迴圈中移動畫布,如果不記得儲存和回復畫布狀態,繪圖區域很容易最後就超出邊界,然後出現圖案不見的狀況。</p> + +<h3 id="A_translate_example" name="A_translate_example">移動畫布範例</h3> + +<p>下面程式碼示範了利用translate()畫圖的好處,裡面,我們用了drawSpirograph()函數畫萬花筒類的圖案,如果沒有移動畫布原點,那麼每個圖案只會有四分之一會落在可視範圍,藉由移動畫布原點我們便可以自由變換每個圖案的位置,使圖案完整出現,而且省去手動計算調整每個圖案的座標位置。</p> + +<p>另外一個draw()函數透過兩個for迴圈移動畫布原點、呼叫drawSpirograph()函數、復歸畫布圓點位置共九次。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + ctx.fillRect(0,0,300,300); + for (var i=0;i<3;i++) { + for (var j=0;j<3;j++) { + ctx.save(); + ctx.strokeStyle = "#9CFF00"; + ctx.translate(50+j*100,50+i*100); + drawSpirograph(ctx,20*(j+2)/(j+1),-8*(i+3)/(i+1),10); + ctx.restore(); + } + } +} + +function drawSpirograph(ctx,R,r,O){ + var x1 = R-O; + var y1 = 0; + var i = 1; + ctx.beginPath(); + ctx.moveTo(x1,y1); + do { + if (i>20000) break; + var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72)) + var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72)) + ctx.lineTo(x2,y2); + x1 = x2; + y1 = y2; + i++; + } while (x2 != R-O && y2 != 0 ); + ctx.stroke(); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="300" height="300"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_translate_example", "330", "330", "https://mdn.mozillademos.org/files/256/Canvas_translate.png")}}</p> + +<h2 id="Rotating" name="Rotating">旋轉</h2> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/233/Canvas_grid_rotate.png" style="float: right;">rotate()函數可以畫布原點作中心,旋轉畫布。</p> + +<dl> + <dt><code>rotate(<em>x</em>)</code></dt> + <dd>以畫布原點為中心,順時針旋轉畫布x弧度(弧度 = Math.PI * 角度 / 180)。</dd> +</dl> + +<p>我們可以呼叫translate()方法來移動旋轉中心(亦即畫布原點)。</p> + +<h3 id="A_rotate_example" name="A_rotate_example">旋轉範例</h3> + +<p>本範例我們呼叫rotate()方法來畫一系列環狀圖案。如果不用rotate(),同樣的效果也可以藉由個別計算x, y座標點(x = r*Math.cos(a); y = r*Math.sin(a))達成;呼叫rotate()和個別計算x, y座標點不同之處在於,個別計算x, y座標點只有旋轉圓形圓心,而圓形並沒有旋轉,呼叫rotate()則會旋轉圓形和圓心,不過因為我們的圖案是圓形,所以兩種作法產生的效果不會有差異。</p> + +<p>我們執行了兩個迴圈來作圖,第一個迴圈決定的圓環個數和該圓環上圓環上圓點的個數的顏色,第二個迴圈決定了圓環上圓點的個數,每一次作圖前我們都儲存了原始畫布狀態,以便結束時可以復原狀態。畫布旋轉的弧度則以圓環上圓點的個數決定,像是最內圈的圓環共有六個圓點,所以每畫一個原點,畫布就旋轉60度(360度/6),第二的圓環有12個原點,所以畫布一次旋轉度數為30度(360度/12),以此類推。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + ctx.translate(75,75); + + for (var i=1;i<6;i++){ // Loop through rings (from inside to out) + ctx.save(); + ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)'; + + for (var j=0;j<i*6;j++){ // draw individual dots + ctx.rotate(Math.PI*2/(i*6)); + ctx.beginPath(); + ctx.arc(0,i*12.5,5,0,Math.PI*2,true); + ctx.fill(); + } + + ctx.restore(); + } +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="150" height="150"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("A_rotate_example", "180", "180", "https://mdn.mozillademos.org/files/248/Canvas_rotate.png")}}</p> + +<h2 id="Scaling" name="Scaling">縮放</h2> + +<p>接下來這一個變形效果為縮放圖形。</p> + +<dl> + <dt>scale(x, y)</dt> + <dd>x代表縮放畫布水平網格單位x倍,y代表縮放畫布垂直網格單位y倍,輸入1.0不會造成縮放。如果輸入負值會造成座標軸鏡射,假設輸入x為-1,那麼原本畫布網格X軸上的正座標點都會變成負座標點、負座標點則變成正座標點。</dd> +</dl> + +<p>只要利用scale(),我們可以建立著名的笛卡兒座標系;執行translate(0,canvas.height)先移動畫布原點到左下角,再執行scale(1,-1)顛倒Y軸正負值,一個笛卡兒座標系便完成了。</p> + +<p>預設上畫布網格前進一單位等於前進一像素大小,所以縮小0.5倍,就會變成前進0.5的像素大小,亦即縮小圖像一半大小,反之,放大2倍將放大圖像2倍。</p> + +<h3 id="A_scale_example" name="A_scale_example">縮放範例</h3> + +<p>本程式碼範例會畫出一系列不同縮放比例的萬花筒樣式圖案。</p> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + ctx.strokeStyle = "#fc0"; + ctx.lineWidth = 1.5; + ctx.fillRect(0,0,300,300); + + // Uniform scaling + ctx.save() + ctx.translate(50,50); + drawSpirograph(ctx,22,6,5); + + ctx.translate(100,0); + ctx.scale(0.75,0.75); + drawSpirograph(ctx,22,6,5); + + ctx.translate(133.333,0); + ctx.scale(0.75,0.75); + drawSpirograph(ctx,22,6,5); + ctx.restore(); + + // Non uniform scaling (y direction) + ctx.strokeStyle = "#0cf"; + ctx.save() + ctx.translate(50,150); + ctx.scale(1,0.75); + drawSpirograph(ctx,22,6,5); + + ctx.translate(100,0); + ctx.scale(1,0.75); + drawSpirograph(ctx,22,6,5); + + ctx.translate(100,0); + ctx.scale(1,0.75); + drawSpirograph(ctx,22,6,5); + ctx.restore(); + + // Non uniform scaling (x direction) + ctx.strokeStyle = "#cf0"; + ctx.save() + ctx.translate(50,250); + ctx.scale(0.75,1); + drawSpirograph(ctx,22,6,5); + + ctx.translate(133.333,0); + ctx.scale(0.75,1); + drawSpirograph(ctx,22,6,5); + + ctx.translate(177.777,0); + ctx.scale(0.75,1); + drawSpirograph(ctx,22,6,5); + ctx.restore(); + +} +function drawSpirograph(ctx,R,r,O){ + var x1 = R-O; + var y1 = 0; + var i = 1; + ctx.beginPath(); + ctx.moveTo(x1,y1); + do { + if (i>20000) break; + var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72)) + var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72)) + ctx.lineTo(x2,y2); + x1 = x2; + y1 = y2; + i++; + } while (x2 != R-O && y2 != 0 ); + ctx.stroke(); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="300" height="300"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>第一排第一個黃色圖案是沒有縮放的圖案,然後往右到了第二個圖案,我們程式碼中輸入了 (0.75, 0.75)的縮放倍率,到了第三個圖案,我們還是輸入了 (0.75, 0.75)的縮放倍率,而基於之前有縮小過一次,所以第三個圖案相對於第一個沒有縮放的圖案是0.75 × 0.75 = 0.5625倍。</p> + +<p>第二排藍色圖案我們只改變Y軸的縮放倍率,X軸維持不變,因而產生一個比一個更扁的橢圓圖形。同理,第三排綠色圖案改變X軸的縮放倍率,Y軸維持不變。</p> + +<p>{{EmbedLiveSample("A_scale_example", "330", "330", "https://mdn.mozillademos.org/files/250/Canvas_scale.png")}}</p> + +<h2 id="Transforms" name="Transforms">變形</h2> + +<p>最後一個方法是設定變形矩陣,藉由改變變形矩陣,我們因此可以營造各種變形效果;其實先前所提到的rotate, translate, scale都是在設定變形矩陣,而這邊的這個方法就是直接去改變變形矩陣。</p> + +<dl> + <dt><code>transform(<em>m11</em>, <em>m12</em>, <em>m21</em>, <em>m22</em>, <em>dx</em>, <em>dy</em>)</code></dt> + <dd>呼叫Transform會拿目前的變形矩陣乘以下列矩陣:</dd> + <dd><img src="http://www.numberempire.com/equation.render?%5Cbegin{bmatrix}%20m11%20&%20m21%20%5C%5C%20m12%20&%20m22%5Cend{bmatrix}%5Cbegin{bmatrix}%20x%20%5C%5C%20y%5Cend{bmatrix}"></dd> + <dd> + <pre>m11 m21 dx +m12 m22 dy +0 0 1 +</pre> + </dd> +</dl> + +<dl> + <dd>運算後的新矩陣將取代目前的變形矩陣。</dd> + <dd>其中m11代表水平縮放圖像,m12代表水平偏移圖像,m21代表垂直偏移圖像,m22代表垂直縮放圖像,dx代表水平移動圖像,dy代表垂直移動圖像。</dd> + <dd>如果輸入<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Infinity" title="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Infinity">Infinity</a></code> 值,不會引起例外錯誤,矩陣值會依照輸入設成無限。</dd> + <dt><code>setTransform(<em>m11</em>, <em>m12</em>, <em>m21</em>, <em>m22</em>, <em>dx</em>, <em>dy</em>)</code></dt> + <dd>復原目前矩陣為恆等矩陣(Identiy matrix,也就是預設矩陣),然後再以輸入參數呼叫transform()。</dd> +</dl> + +<h3 id="transform_.2F_setTransform_examples" name="transform_.2F_setTransform_examples"><code>transform</code> / <code>setTransform</code>範例</h3> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + + var sin = Math.sin(Math.PI/6); + var cos = Math.cos(Math.PI/6); + ctx.translate(100, 100); + var c = 0; + for (var i=0; i <= 12; i++) { + c = Math.floor(255 / 12 * i); + ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")"; + ctx.fillRect(0, 0, 100, 10); + ctx.transform(cos, sin, -sin, cos, 0, 0); + } + + ctx.setTransform(-1, 0, 0, 1, 100, 100); + ctx.fillStyle = "rgba(255, 128, 255, 0.5)"; + ctx.fillRect(0, 50, 100, 100); +} +</pre> + +<div class="hidden"> +<pre class="brush: html"><canvas id="canvas" width="200" height="250"></canvas></pre> + +<pre class="brush: js">draw();</pre> +</div> + +<p>{{EmbedLiveSample("transform_.2F_setTransform_examples", "230", "280", "https://mdn.mozillademos.org/files/255/Canvas_transform.png")}}</p> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors", "Web/Guide/HTML/Canvas_tutorial/Compositing")}}</p> diff --git a/files/zh-tw/web/api/canvas_api/tutorial/using_images/index.html b/files/zh-tw/web/api/canvas_api/tutorial/using_images/index.html new file mode 100644 index 0000000000..7bb6bf791f --- /dev/null +++ b/files/zh-tw/web/api/canvas_api/tutorial/using_images/index.html @@ -0,0 +1,342 @@ +--- +title: 使用影像 +slug: Web/API/Canvas_API/Tutorial/Using_images +translation_of: Web/API/Canvas_API/Tutorial/Using_images +--- +<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_text", "Web/API/Canvas_API/Tutorial/Transformations" )}}</div> + +<p><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">使用影像是</font></span></font>{{HTMLElement("canvas")}}<font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">另一個有趣的功能,這個功能可以用來動態組合圖片或作為背景等等。任何瀏覽器支援的外部圖片格式都可以使用,例如</font></span></font><font face="DejaVu Sans, sans-serif">PNG, GIF, </font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">或</font></span></font><font face="DejaVu Sans, sans-serif">JPEG</font><font face="DejaVu Sans"><span lang="zh-TW"><font face="DejaVu Sans, sans-serif">,甚至也可以利用同一份頁面上其他畫布元素產生的影像.</font></span></font></p> + +<p><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">載入影像到畫布中基本上需要兩個步驟:</span></font></p> + +<ol> + <li> + <p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">取得</span></font><font face="DejaVu Sans, sans-serif">{{domxref("HTMLImageElement")}}</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">物件或其他畫布元素的參照</span></font><font face="DejaVu Sans, sans-serif">(reference)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">作為來源,透過單純提供</span></font><font face="DejaVu Sans, sans-serif">URL</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">或圖片位置的方式是行不通的</span></font>.</p> + </li> + <li> + <p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">用</span></font><font face="DejaVu Sans, sans-serif">drawImage()</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">函數在畫布上畫影像</span></font>.</p> + </li> +</ol> + +<p><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">接下來便來看看要怎麼做</span></font>.</p> + +<h2 id="取得影像"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">取得影像</span></font></h2> + +<p><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">畫布</span></font><font face="DejaVu Sans, sans-serif">API</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">能接受以下資料型態作為影像來源</span></font>:</p> + +<dl> + <dt>{{domxref("HTMLImageElement")}}</dt> + <dd> + <p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">用</span></font><font face="DejaVu Sans, sans-serif">Image()</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">建構成的影像或是</span></font><font face="DejaVu Sans, sans-serif">{{HTMLElement("img")}}</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素</span></font>.</p> + </dd> + <dt>{{domxref("HTMLVideoElement")}}</dt> + <dd> + <p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">用</span></font><font face="DejaVu Sans, sans-serif">{{domxref("HTMLVideoElement")}}</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素作影像來源,抓取影片目前的影像畫格當作影像使用</span></font>.</p> + </dd> + <dt>{{domxref("HTMLCanvasElement")}}</dt> + <dd> + <p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">用另一個</span></font><font face="DejaVu Sans, sans-serif">{{domxref("HTMLCanvasElement")}}</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">元素當影像來源</span></font>.</p> + </dd> + <dt>{{domxref("ImageBitmap")}}</dt> + <dd> + <p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">可以被快速渲染的點陣圖</span></font><font face="DejaVu Sans, sans-serif">(bitmap)</font><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">,點陣圖能由上述所有來源產生</span></font>.</p> + </dd> +</dl> + +<p><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">這些來源統一參照</span></font> <font face="DejaVu Sans, sans-serif"><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#image-sources-for-2d-rendering-contexts" title="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#image-sources-for-2d-rendering-contexts">CanvasImageSource</a><span lang="zh-TW">型態</span></font>.</p> + +<p style="margin-bottom: 0in;"><font face="DejaVu Sans, sans-serif"><span lang="zh-TW">有好幾種方法能夠取得影像用於畫布<font face="DejaVu Sans, sans-serif">.</font></span></font></p> + +<p style="margin-bottom: 0in;"> </p> + +<h3 id="使用同一份網頁上的影像">使用同一份網頁上的影像</h3> + +<p>我們能透過下面幾個方法取得影像:</p> + +<ul> + <li>從{{domxref("document.images")}}</li> + <li>{{domxref("document.getElementsByTagName()")}}方法</li> + <li>如果知道特定影像的ID,那可以{{domxref("document.getElementById()")}}方法</li> +</ul> + +<h3 id="使用來自其他網域的影像">使用來自其他網域的影像</h3> + +<p>Using the <a href="/en-US/docs/HTML/CORS_settings_attributes" rel="internal" title="CORS settings attributes"><code>crossOrigin</code></a> attribute on an 透過{{HTMLElement("HTMLImageElement")}}的<a href="/en-US/docs/HTML/CORS_settings_attributes" rel="internal" title="CORS settings attributes"><code>crossOrigin</code></a>屬性, 我們可以要求從另一個網域載入影像來使用,若是寄存網域(thehosting domain)准許跨網路存取該影像,那麼我們便可以使用它而不用污染(taint)我們的畫布,反之,使用該影像會污染畫布(<a href="https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image#What_is_a_.22tainted.22_canvas.3F" rel="internal" title="en/CORS_Enabled_Image#What_is_a_.22tainted.22_canvas.3F">taint the canvas</a>)。</p> + +<h3 id="使用其他畫布元素">使用其他畫布元素</h3> + +<p>如同取得其他影像,我們一樣能用{{domxref("document.getElementsByTagName()")}}或{{domxref("document.getElementById()")}}方法取得其他畫布元素,但是在使用之前請記得來源畫布上已經有繪上圖了。</p> + +<p>使用其他畫布元素作為影像來源有很多有用的應用用途,其中之一便是建立第二個小畫布作為另一個大畫布的縮小影像.</p> + +<h3 id="創造全新的影像">創造全新的影像</h3> + +<p>產生新的{{domxref("HTMLImageElement")}}物件也能當作影像來源,這邊,我們可以用Image()來建構一個新影像元素:</p> + +<pre class="brush: js">var img = new Image(); // Create new img element +img.src = 'myImage.png'; // Set source path +</pre> + +<p>上述程式碼執行後會載入影像.</p> + +<p>在影像載入完成前呼叫drawImage()不會有任何效果,甚至某些瀏覽器還會拋出例外狀況,所以應該要透過利用載入事件來避免這類問題:</p> + +<pre class="brush: js">var img = new Image(); // Create new img element +img.addEventListener("load", function() { + // execute drawImage statements here +}, false); +img.src = 'myImage.png'; // Set source path +</pre> + +<p>若是只要載入一份影像,可以用上面的方法,不過當需要載入、追蹤多個影像時,我們就需要更好的方法了,雖然管理多個影像載入已經超出本教學的範疇,然而如果有興趣的話,可以參考<a class="external" href="http://www.webreference.com/programming/javascript/gr/column3/" rel="external" title="http://www.webreference.com/programming/javascript/gr/column3/">JavaScript Image Preloader</a>這份文件.</p> + +<h3 id="以dataURL嵌入影像">以data:URL嵌入影像</h3> + +<p>另一個載入影像的方法是利用<a class="external" href="/en-US/docs/data_URIs" rel="external" title="http://en.wikipedia.org/wiki/Data:_URL">data: url</a>,透過data URL可以直接將影像定義成Base64編碼的字串,然後嵌入程式碼之中.</p> + +<pre class="js">var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw=='; +</pre> + +<p>data URL的好處之一是立即產生影像而不用再和伺服器連線,另一個好處是這樣便能夠將影像包入你的<a href="/en-US/docs/Web/CSS" title="/en-US/docs/Web/CSS">CSS</a>, <a href="/en-US/docs/Web/JavaScript" title="/en-US/docs/Web/JavaScript">JavaScript</a>, <a href="/en-US/docs/Web/HTML" title="/en-US/docs/Web/HTML">HTML</a>之中,讓影像更具可攜性.</p> + +<p>壞處則是影像將不會被快取起來,而且對大影像來說編碼後的URL會很長.</p> + +<h3 id="Using_frames_from_a_video">Using frames from a video</h3> + +<p>我們還能夠使用{{HTMLElement("video")}}元素中的影片的影片畫格(縱使影片為隱藏),例如,現在我們有一個ID為”myvideo” 的{{HTMLElement("video")}}元素:</p> + +<pre class="brush: js">function getMyVideo() { + var canvas = document.getElementById('canvas'); + if (canvas.getContext) { + var ctx = canvas.getContext('2d'); + + return document.getElementById('myvideo'); + } +} +</pre> + +<p>上面的方法會回傳一個{{domxref("HTMLVideoElement")}}的影像物件,如前所述,這個物件可以被視為CanvasImageSource類別的物件來使用。<br> + 關於如何利用<video>元素於畫布上的進階說明,可以參考html5Doctor的“<a href="http://html5doctor.com/video-canvas-magic/" title="http://html5doctor.com/video-canvas-magic/">video + canvas = magic</a>”一文.</p> + +<h2 id="影像繪圖">影像繪圖</h2> + +<p>一旦我們取得來源影像物件的參照(reference),便可以用drawImage()方法將影像渲染到畫布上,drawImage()方法是一個多載(overload)方法,有數個型態,待會我們會看到這項特性,現在我們先來看drawImage()最基本的型態:</p> + +<dl> + <dt><code>drawImage(<em>image</em>, <em>x</em>, <em>y</em>)</code></dt> + <dd>從座標點(x, y)開始畫上image參數指定的來源影像(CanvasImageSource).</dd> +</dl> + +<h3 id="範例_一條簡單的線段影像">範例: 一條簡單的線段影像</h3> + +<p>這個範例會使用外部影像作為一個小型線圖的背景。利用預先劃好的圖作為背景的話就不用再靠程式來產生背景,如此一來可以顯著地減少程式碼。下面藉由影像物件的load事件處理器來處理繪圖作業,其中drawImage()方法把背景圖片放置在畫布左上角,座標點(0, 0)位置.</p> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="180" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + img.onload = function(){ + ctx.drawImage(img,0,0); + ctx.beginPath(); + ctx.moveTo(30,96); + ctx.lineTo(70,66); + ctx.lineTo(103,76); + ctx.lineTo(170,15); + ctx.stroke(); + }; + img.src = 'https://mdn.mozillademos.org/files/5395/backdrop.png'; +}</pre> + +<p>結果如下:</p> + +<p>{{EmbedLiveSample("Example.3A_A_simple_line_graph", 220, 160, "https://mdn.mozillademos.org/files/206/Canvas_backdrop.png")}}</p> + +<h2 id="縮放">縮放</h2> + +<p>drawImage()的第二個型態增加了兩個新參數,讓我們在畫布上放置影像的同時並縮放影像.</p> + +<dl> + <dt><code>drawImage(<em>image</em>, <em>x</em>, <em>y</em>, <em>width</em>, <em>height</em>)</code></dt> + <dd>當放置影像於畫布上時,會按照參數width(寬)、height(高)來縮放影像.</dd> +</dl> + +<h3 id="範例_排列影像">範例: 排列影像</h3> + +<p>本例我們會取一張影像作為桌布,然後透過簡單的迴圈來重複縮放、貼上影像於畫布上。在程式碼中,第一個迴圈走一遍每一列,第二個迴圈走一遍每一行,影像則縮小成原始影像的三分之一,50 x 38像素.</p> + +<div class="note"> +<p><strong>Note</strong>: 過度縮放影像可能會造成影像模糊或產生顆粒感,所以如果影像中有文字需要閱讀,最好不要縮放影像.</p> +</div> + +<div class="hidden"> +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + </body> +</html> +</pre> +</div> + +<pre class="brush: js">function draw() { + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + img.onload = function(){ + for (var i=0;i<4;i++){ + for (var j=0;j<3;j++){ + ctx.drawImage(img,j*50,i*38,50,38); + } + } + }; + img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; +}</pre> + +<p>結果如下:</p> + +<p>{{EmbedLiveSample("Example.3A_Tiling_an_image", 160, 160, "https://mdn.mozillademos.org/files/251/Canvas_scale_image.png")}}</p> + +<h2 id="切割影像">切割影像</h2> + +<p>drawImage()第三個型態接受9個參數,其中8個讓我們從原始影像中切出一部分影像、縮放並畫到畫布上.</p> + +<dl> + <dt><code>drawImage(<em>image</em>, <em>sx</em>, <em>sy</em>, <em>sWidth</em>, <em>sHeight</em>, <em>dx</em>, <em>dy</em>, <em>dWidth</em>, <em>dHeight</em>)</code></dt> + <dd>image參數是來源影像物件,(sx, sy)代表在來源影像中以(sx, sy)座標點作為切割的起始點,sWidth和sHeight代表切割寬和高,(dx, dy)代表放到畫布上的座標點,dWidth和dHeight代表縮放影像至指定的寬和高.</dd> +</dl> + +<p><img alt="" class="internal" src="https://mdn.mozillademos.org/files/225/Canvas_drawimage.jpg" style="float: right; height: 290px; width: 300px;">請參照右圖,前四個參數定義了在來源影像上切割的起始點和切割大小,後四個參數定義了畫到畫布上的位置和影像大小.</p> + +<p>切割是一個很有用的工具,我們可以把所有影像放到一張影像上,然後利用切割來組成最終完整的影像,比如說,我們可以把所有需要用來組成一張圖表的文字放到一張PNG圖檔內,之後只需要單純地再依據資料來縮放圖表,另外,我們也不用多次載入多張影像,這樣對提升載入影像效能頗有幫助.</p> + +<p> </p> + +<p> </p> + +<h3 id="範例_畫一個有畫框的影像">範例: 畫一個有畫框的影像</h3> + +<p>本例用和前一個範例一樣的犀牛圖,然後切出犀牛頭部影像部分再放入一個影像畫框,這個影像畫框是一個有陰影的24位元PNG圖檔,因為24位元PNG影像具備完整的8位元不透明色版(alpha channel),所以不像GIF影像和8位元PNG影像,它能夠放任何背景之上而無須擔心產生消光色(matte color).</p> + +<pre class="brush: html"><html> + <body onload="draw();"> + <canvas id="canvas" width="150" height="150"></canvas> + <div style="display:none;"> + <img id="source" src="https://mdn.mozillademos.org/files/5397/rhino.jpg" width="300" height="227"> + <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150"> + </div> + </body> +</html> +</pre> + +<pre class="brush: js">function draw() { + var canvas = document.getElementById('canvas'); + var ctx = canvas.getContext('2d'); + + // Draw slice + ctx.drawImage(document.getElementById('source'), + 33, 71, 104, 124, 21, 20, 87, 104); + + // Draw frame + ctx.drawImage(document.getElementById('frame'),0,0); +}</pre> + +<p>這次我們不產生新的{{domxref("HTMLImageElement")}}物件,改採用直接把影像包入HTML的{{HTMLElement("img")}}標籤,然後再取得影像元素,其中HTML上的影像已經透過設定CSS屬性{{cssxref("display")}}為none來隱藏起來了.</p> + +<p>{{EmbedLiveSample("Example.3A_Framing_an_image", 160, 160, "https://mdn.mozillademos.org/files/226/Canvas_drawimage2.jpg")}}</p> + +<p>程式碼相當簡單,每個{{HTMLElement("img")}}有自己的ID屬性,這樣便可以利用{{domxref("document.getElementById()")}}輕易取得,之後再簡單地用drawImage()方法切割犀牛影像然後縮放並放到畫布上,最後第二個drawImage()再把畫框放到上面.</p> + +<h2 id="畫廊範例">畫廊範例</h2> + +<p>在本章的最後一個範例,我們將建造一個小畫廊。當網頁載入完成時,我們會為每一張影像產生一個{{HTMLElement("canvas")}}元素,並且加上畫框.</p> + +<p>本範例中,每一張影像的寬高是固定的,畫框也是一樣,你可以嘗試看看改進程式碼,依據影像的寬高來設定畫框,使畫框能剛剛好框住影像.</p> + +<p>從下方的程式碼範例可以很清楚看到,我們為{{domxref("document.images")}}容器內的影像,一張一張地新建畫布,其中,對於不熟悉文件物件模型 (DOM)的人來說,大慨比較值得注意之處在於使用到{{domxref("Node.insertBefore")}} 方法;insertBefore()是影像元素的父節點(亦即<td>元素)的一個方法,這個方法會把新畫布元素插入於影像元素之前.</p> + +<pre class="brush: html"><html> + <body onload="draw();"> + <table> + <tr> + <td><img src="https://mdn.mozillademos.org/files/5399/gallery_1.jpg"></td> + <td><img src="https://mdn.mozillademos.org/files/5401/gallery_2.jpg"></td> + <td><img src="https://mdn.mozillademos.org/files/5403/gallery_3.jpg"></td> + <td><img src="https://mdn.mozillademos.org/files/5405/gallery_4.jpg"></td> + </tr> + <tr> + <td><img src="https://mdn.mozillademos.org/files/5407/gallery_5.jpg"></td> + <td><img src="https://mdn.mozillademos.org/files/5409/gallery_6.jpg"></td> + <td><img src="https://mdn.mozillademos.org/files/5411/gallery_7.jpg"></td> + <td><img src="https://mdn.mozillademos.org/files/5413/gallery_8.jpg"></td> + </tr> + </table> + <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150"> + </body> +</html> +</pre> + +<p>這些是一些設定樣式的CSS:</p> + +<pre class="brush: css">body { + background: 0 -100px repeat-x url(https://mdn.mozillademos.org/files/5415/bg_gallery.png) #4F191A; + margin: 10px; +} + +img { + display: none; +} + +table { + margin: 0 auto; +} + +td { + padding: 15px; +} +</pre> + +<p>綜合起來這就是建造出我們小畫廊的程式碼:</p> + +<pre class="brush: js">function draw() { + + // Loop through all images + for (var i=0;i<document.images.length;i++){ + + // Don't add a canvas for the frame image + if (document.images[i].getAttribute('id')!='frame'){ + + // Create canvas element + canvas = document.createElement('canvas'); + canvas.setAttribute('width',132); + canvas.setAttribute('height',150); + + // Insert before the image + document.images[i].parentNode.insertBefore(canvas,document.images[i]); + + ctx = canvas.getContext('2d'); + + // Draw image to canvas + ctx.drawImage(document.images[i],15,20); + + // Add frame + ctx.drawImage(document.getElementById('frame'),0,0); + } + } +}</pre> + +<p>{{EmbedLiveSample("Art_gallery_example", 725, 400, "https://mdn.mozillademos.org/files/205/Canvas_art_gallery.jpg")}}</p> + +<h2 id="控制影像縮放行為">控制影像縮放行為</h2> + +<p>如前所述,縮放影像會導致影像模糊化或是顆粒化,你可以嘗試透過繪圖環境的imageSmoothingEnabled屬性來控制影像平滑演算法的使用,預設上這個屬性的值為true,也就是說當縮放時,影像會經過平滑處理。如果要關掉這個功能,你可以這麼做:</p> + +<pre class="js">ctx.mozImageSmoothingEnabled = false; +</pre> + +<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Drawing_shapes", "Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors")}}</p> |