使用影像

  • 版本網址代稱: Canvas_教學/使用影像
  • 版本標題: 使用影像
  • 版本 ID: 274215
  • 建立日期:
  • 建立者: happysadman
  • Is current revision?
  • 回應 1 words added, 1 words removed

版本內容

Canvas 其中一項有趣的功能就是能夠使用影像。可用於動態的組合相片,或用作為圖形的背景等等。這也是目前唯一的新增文字的途徑(規格書裡並沒有任何繪製文字的函數)。只要是 Gecko 支援的影像格式(如 PNG、GIF 或 JPEG 格式)就都能使用。也能把同一頁面上的 canvas 元素作為影像來源。

匯入影像

匯入影像基本上只需兩個步驟︰

  • 首先,我們需要一個 JavaScript Image 物件的參考,或者以其他 canvas 元素作為來源。而不能簡單的以 URL 或路徑直接使用影像。
  • 其次,我們使用 drawImage 函數把影像畫在 canvas 上。

首先讓我們來看看第一個步驟。基本上有四個可行的選擇︰

使用同一頁面上的影像

我們可以使用各種方式存取同一頁面上的任一影像,例如 document.images 集合,document.getElementsByTagName 方法,如果你知道特定影像的 ID 值,也可以使用 document.getElementById 方法。

使用其他的 canvas 元件

如同存取頁面上的普通影像一般,我們也可以使用各種方式存取 canvas 元素,例如 document.getElementsByTagName 方法,document.getElementById 方法。在使用這些 Canvas 之前,應先確定你已經在裡面繪製了一些東西。

本方法更為實際的應用是使用第二個 canvas 元素作為另一個較大的 canvas 的縮圖。

從頭建立影像

另一個選擇是在我們的 script 裡建立新的 Image 物件。這個方法主要的缺點是,如果我們不希望我們的 script 停在載入影像途中,便需要預先載入影像。

建立新的 image 物件如下︰

var img = new Image();   // 建立新的 image 物件
img.src = 'myImage.png'; // 設定來源路徑

當 script 執行時,開始載入影像。如果未能在執行 drawImage 之前載入完成,script 會停住,直到影像載入完成。如果你不希望這樣的話,使用 onload 事件處理器︰

var img = new Image();   // 建立新的 image 物件
img.onload = function(){
  // 執行 drawImage 的語句
}
img.src = 'myImage.png'; // 設定來源路徑

如果你只使用一個外部的影像,這就是個不錯的解決方法。但如果一次不只一個的話,便需要使用一些技巧。影像預讀策略已超出了本教學的範圍,不過你可以參考 JavaScript Image Preloader 以取得完整的解決方法。

使用 data: url 嵌入影像

另一個可行的方法是使用 data: url 包含影像。Data url 可讓你直接在代碼中定義影像,影像是以 Base64 編碼成字串。data url 的優點是影像立即可用,無須再次存取伺服器。(另一個優點是,可以把 CSS、Javascript、HTML 和影像全部裝進一個檔案之內,更具可攜性。)這個方法的缺點是,你的影像不能快取,對於大型影像來說,經過編碼的 url 會變的很長︰

var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';

 

drawImage

一旦取得作為來源的影像物件,我們就可以使用 drawImage 方法畫到 canvas 上。稍後我們將會看到經過重載(Overload)的 drawImage 方法的三個變體。它的最簡形式如下。

drawImage(image, x, y)

此處 image 即影像或 canvas 物件的參考。xy 坐標即影像的擺放位置。

drawImage 範例 1

在接下來的範例中,我會使用一個外部的影像作為線條圖的背景。背景的使用可以大幅節省 Script 內容,因為無需再繪製細緻的背景。在此我只使用一張影像,所以我使用 Image 物件的 onload 事件處理器執行繪圖語句。drawImage 方法會把背景放在 (0,0) 坐標處。

檢視範例

  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 = 'images/backdrop.png';
  }

縮放

drawImage 方法的第二個變體多了兩個用於縮放影像的參數。

drawImage(image, x, y, width, height)

此處的 widthheight 是影像在 canvas 上的大小。

drawImage 範例 2

在這個範例中,我將使用一個影像作為 canvas 的桌布,並重覆若干次。只需使用迴圈,就能輕鬆的把縮小後的影像放置在不同的位置。下面代碼的第一個 for 迴圈走遍每一行,第二個 for 迴圈走遍每一列。影像縮小成原來的三分之一,像素為 50x38。後面的教學中,我們還會看到如何使用自訂圖樣(pattern)達到同樣的效果。

附註︰放大影像會使影像模糊,過度縮小影像則會使影像出現波紋。如果影像上已有一些文字,為清晰易讀起見,這時最好不要縮放影像。

檢視範例 Source image

  function draw() {
    var ctx = document.getElementById('canvas').getContext('2d');
    var img = new Image();
    img.onload = function(){
      for (i=0;i<4;i++){
        for (j=0;j<3;j++){
          ctx.drawImage(img,j*50,i*38,50,38);
        }
      }
    }
    img.src = 'images/rhino.jpg';
  }

切片

drawImage 方法的最後一個變體有八個新的參數。我們可以使用這個方法把來源影像切一塊下來,並繪製在 canvas 上。

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

第一個參數 image 和其他變體一樣,只是 Image 物件的參考,或是其他的 canvas 元素。至於剩下的八個參數,最好看看右邊的圖例。前四個參數定義了來源影像的切片位置和大小。後四個參數定義了要畫在 canvas 的位置和大小。

如果你想要組合一些東西,切片會是個很有用的工具。你可以把所有的元件全都放進同一影像檔裡,再使用切片方法組合畫面。舉個例子,如果你想要製做圖表,可以把所有所需的文字全都放進一個 PNG 影像檔裡,並根據你的數據改變圖表的比例。其優點是不再需要分別載入每一個影像。

drawImage 範例 3

在這個範例中,我會使用之前已看過的犀牛圖,不過這次我會切下頭部,並把切下的圖組進畫框裡。畫框影像還附有陰影,並儲存為 32 位元的 PNG 檔案。由於 32 位元的 PNG 影像包含有完整的 8 位元 Alpha 通道,有別於 GIF 和 8 位元 PNG 影像,我可以把它放在背景圖上面,而不必擔心邊緣的顏色。

我改用不同的方式載入影像。我直接在 HTML 文件上放置影像,並使用 CSS 規則隱藏影像(display:none)。我給兩個影像各指定一個 id 屬性,以便選取。Script 本身很簡單。我先在 canvas 上畫出切下並縮小過的影像(第一個 drawImage 語句),然後加上畫框(第二個 drawImage 語句)。

檢視範例 Source image

function draw() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  // 繪製切片
  ctx.drawImage(document.getElementById('source'),
                33,71,104,124,21,20,87,104);

  // 繪製畫框
  ctx.drawImage(document.getElementById('frame'),0,0);
}

藝術圖庫範例

在本章的最後一個範例裡,我會製作一些藝術圖庫。圖庫以包含若干圖像的表格組合。當頁面載入完畢時,就在頁面上的每一個圖像之前插入加上畫框的 canvas 元素。

在本例中,所有的圖像都有固定的寬和高,並在邊緣加上畫框。你可以修改 Script,使畫框配合圖像的大小。

以下的代碼應該很容易看懂。迴圈遍歷陣列裡的所有圖像,並為圖像加上新的 canvas 元素。對於那些不熟悉 DOM 的人來說,可能還有一處需要注意,那就是 insertBefore 方法的使用insertBefore 是圖像元素的親節點(表格)的方法,我們打算在圖像元素前面插入新的結點(canvas 元素)。

檢視範例

function draw() {

  // 遍歷所有圖像的迴圈
  for (i=0;i<document.images.length;i++){

    // 無須為畫框圖像加上 canvas
    if (document.images[i].getAttribute('id')!='frame'){

      // 建立 canvas 元素
      canvas = document.createElement('CANVAS');
      canvas.setAttribute('width',132);
      canvas.setAttribute('height',150);

      // 在圖像之前插入
      document.images[i].parentNode.insertBefore(canvas,document.images[i]);

      ctx = canvas.getContext('2d');

      // 把圖像畫在 canvas 上
      ctx.drawImage(document.images[i],15,20);

      // 加上畫框
      ctx.drawImage(document.getElementById('frame'),0,0);
    }
  }
}

{{ PreviousNext("Canvas 教學:繪製圖形", "Canvas 教學:套用樣式和色彩") }}

{{ languages( { "en": "en/Canvas_tutorial/Using_images", "fr": "fr/Tutoriel_canvas/Utilisation_d\'images", "ja": "ja/Canvas_tutorial/Using_images", "pl": "pl/Przewodnik_po_canvas/Zastosowanie_obrazk\u00f3w", "zh-cn": "cn/Canvas_tutorial/Using_images", "ru": "ru/\u041e\u0431\u0443\u0447\u0435\u043d\u0438\u0435_canvas/\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435_\u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a" } ) }}

版本來源

<p>Canvas 其中一項有趣的功能就是能夠使用影像。可用於動態的組合相片,或用作為圖形的背景等等。這也是目前唯一的新增文字的途徑(規格書裡並沒有任何繪製文字的函數)。只要是 <a class="internal" href="/zh_tw/Gecko" title="zh tw/Gecko">Gecko</a> 支援的影像格式(如 PNG、GIF 或 JPEG 格式)就都能使用。也能把同一頁面上的 canvas 元素作為影像來源。</p>
<h2 name="Importing_images">匯入影像</h2>
<p>匯入影像基本上只需兩個步驟︰</p>
<ul> <li>首先,我們需要一個 JavaScript Image 物件的參考,或者以其他 canvas 元素作為來源。而不能簡單的以 URL 或路徑直接使用影像。</li> <li>其次,我們使用 <code>drawImage</code> 函數把影像畫在 canvas 上。</li>
</ul>
<p>首先讓我們來看看第一個步驟。基本上有四個可行的選擇︰</p>
<h4 name="Using_images_which_are_on_the_same_page">使用同一頁面上的影像</h4>
<p>我們可以使用各種方式存取同一頁面上的任一影像,例如 <code><a href="/en/DOM/document.images" title="en/DOM/document.images">document.images</a></code> 集合,<code><a href="/en/DOM/document.getElementsByTagName" title="en/DOM/document.getElementsByTagName">document.getElementsByTagName</a></code> 方法,如果你知道特定影像的 ID 值,也可以使用 <code><a href="/en/DOM/document.getElementById" title="en/DOM/document.getElementById">document.getElementById</a></code> 方法。</p>
<h4 name="Using_other_canvas_elements">使用其他的 canvas 元件</h4>
<p>如同存取頁面上的普通影像一般,我們也可以使用各種方式存取 canvas 元素,例如 <code><a href="/en/DOM/document.getElementsByTagName" title="en/DOM/document.getElementsByTagName">document.getElementsByTagName</a></code> 方法,<code><a href="/en/DOM/document.getElementById" title="en/DOM/document.getElementById">document.getElementById</a></code> 方法。在使用這些 Canvas 之前,應先確定你已經在裡面繪製了一些東西。</p>
<p>本方法更為實際的應用是使用第二個 canvas 元素作為另一個較大的 canvas 的縮圖。</p>
<h4 name="Creating_an_image_from_scratch">從頭建立影像</h4>
<p>另一個選擇是在我們的 script 裡建立新的 <code>Image</code> 物件。這個方法主要的缺點是,如果我們不希望我們的 script 停在載入影像途中,便需要預先載入影像。</p>
<p>建立新的 image 物件如下︰</p>
<pre class="brush: js">var img = new Image();   // 建立新的 image 物件
img.src = 'myImage.png'; // 設定來源路徑
</pre>
<p>當 script 執行時,開始載入影像。如果未能在執行 <code>drawImage</code> 之前載入完成,script 會停住,直到影像載入完成。如果你不希望這樣的話,使用 <code>onload</code> 事件處理器︰</p>
<pre class="brush: js">var img = new Image();   // 建立新的 image 物件
img.onload = function(){
  // 執行 drawImage 的語句
}
img.src = 'myImage.png'; // 設定來源路徑
</pre>
<p>如果你只使用一個外部的影像,這就是個不錯的解決方法。但如果一次不只一個的話,便需要使用一些技巧。影像預讀策略已超出了本教學的範圍,不過你可以參考 <a class="external" href="http://www.webreference.com/programming/javascript/gr/column3/">JavaScript Image Preloader</a> 以取得完整的解決方法。</p>
<h4 name="Embedding_an_image_via_data:_url">使用 data: url 嵌入影像</h4>
<p>另一個可行的方法是使用 <a class="external" href="http://en.wikipedia.org/wiki/Data:_URL">data: url</a> 包含影像。Data url 可讓你直接在代碼中定義影像,影像是以 Base64 編碼成字串。data url 的優點是影像立即可用,無須再次存取伺服器。(另一個優點是,可以把 CSS、Javascript、HTML 和影像全部裝進一個檔案之內,更具可攜性。)這個方法的缺點是,你的影像不能快取,對於大型影像來說,經過編碼的 url 會變的很長︰</p>
<pre class="brush: js">var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
</pre>
<p> </p>
<h2 name="drawImage"><code>drawImage</code></h2>
<p>一旦取得作為來源的影像物件,我們就可以使用 <code>drawImage</code> 方法畫到 canvas 上。稍後我們將會看到經過重載(Overload)的 <code>drawImage</code> 方法的三個變體。它的最簡形式如下。</p>
<div style="border: 1px solid rgb(208, 221, 158); background: rgb(239, 248, 206) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; padding-left: 10px;">
<p><code><strong>drawImage</strong>(image, x, y)</code></p>
</div>
<p>此處 <code>image</code> 即影像或 canvas 物件的參考。<code>x</code> 和 <code>y</code> 坐標即影像的擺放位置。</p>
<h4 name="drawImage_example_1"><code>drawImage</code> 範例 1</h4>
<p><img align="right" alt="" class="internal" src="/@api/deki/files/58/=Canvas_backdrop.png">在接下來的範例中,我會使用一個外部的影像作為線條圖的背景。背景的使用可以大幅節省 Script 內容,因為無需再繪製細緻的背景。在此我只使用一張影像,所以我使用 Image 物件的 <code>onload</code> 事件處理器執行繪圖語句。<code>drawImage</code> 方法會把背景放在 (0,0) 坐標處。</p>
<p><a class="external" href="/samples/canvas-tutorial/3_1_canvas_drawimage.html" title="samples/canvas-tutorial/3_1_canvas_drawimage.html">檢視範例</a></p>
<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 = 'images/backdrop.png';
  }
</pre>
<h2 name="Scaling">縮放</h2>
<p><code>drawImage</code> 方法的第二個變體多了兩個用於縮放影像的參數。</p>
<div style="border: 1px solid rgb(208, 221, 158); background: rgb(239, 248, 206) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; padding-left: 10px;">
<p><code><strong>drawImage</strong>(image, x, y, width, height)</code></p>
</div>
<p>此處的 <code>width</code> 和 <code>height</code> 是影像在 canvas 上的大小。</p>
<h4 name="drawImage_example_2"><code>drawImage</code> 範例 2</h4>
<p><img align="right" alt="" class="internal" src="/@api/deki/files/106/=Canvas_scale_image.png"> 在這個範例中,我將使用一個影像作為 canvas 的桌布,並重覆若干次。只需使用迴圈,就能輕鬆的把縮小後的影像放置在不同的位置。下面代碼的第一個 <code>for</code> 迴圈走遍每一行,第二個 <code>for</code> 迴圈走遍每一列。影像縮小成原來的三分之一,像素為 50x38。後面的教學中,我們還會看到如何使用自訂圖樣(pattern)達到同樣的效果。</p>
<p><strong>附註︰</strong>放大影像會使影像模糊,過度縮小影像則會使影像出現波紋。如果影像上已有一些文字,為清晰易讀起見,這時最好不要縮放影像。</p>
<p><a class="external" href="/samples/canvas-tutorial/3_2_canvas_drawimage.html" title="samples/canvas-tutorial/3_2_canvas_drawimage.html">檢視範例</a> <img align="right" alt="Source image" class="internal" src="/@api/deki/files/101/=Canvas_rhino.jpg"></p>
<pre class="brush: js">  function draw() {
    var ctx = document.getElementById('canvas').getContext('2d');
    var img = new Image();
    img.onload = function(){
      for (i=0;i&lt;4;i++){
        for (j=0;j&lt;3;j++){
          ctx.drawImage(img,j*50,i*38,50,38);
        }
      }
    }
    img.src = 'images/rhino.jpg';
  }
</pre>
<h2 name="Slicing">切片</h2>
<p><code>drawImage</code> 方法的最後一個變體有八個新的參數。我們可以使用這個方法把來源影像切一塊下來,並繪製在 canvas 上。</p>
<div style="border: 1px solid rgb(208, 221, 158); background: rgb(239, 248, 206) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; padding-left: 10px; margin-bottom: 10px;">
<p><code><strong>drawImage</strong>(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)</code></p>
</div>
<p><img align="right" alt="" class="internal" src="/@api/deki/files/79/=Canvas_drawimage.jpg">第一個參數 <code>image</code> 和其他變體一樣,只是 Image 物件的參考,或是其他的 canvas 元素。至於剩下的八個參數,最好看看右邊的圖例。前四個參數定義了來源影像的切片位置和大小。後四個參數定義了要畫在 canvas 的位置和大小。</p>
<p>如果你想要組合一些東西,切片會是個很有用的工具。你可以把所有的元件全都放進同一影像檔裡,再使用切片方法組合畫面。舉個例子,如果你想要製做圖表,可以把所有所需的文字全都放進一個 PNG 影像檔裡,並根據你的數據改變圖表的比例。其優點是不再需要分別載入每一個影像。</p>
<h4 name="drawImage_example_3"><code>drawImage</code> 範例 3</h4>
<p><img align="right" alt="" class="internal" src="/@api/deki/files/80/=Canvas_drawimage2.jpg">在這個範例中,我會使用之前已看過的犀牛圖,不過這次我會切下頭部,並把切下的圖組進畫框裡。畫框影像還附有陰影,並儲存為 32 位元的 PNG 檔案。由於 32 位元的 PNG 影像包含有完整的 8 位元 Alpha 通道,有別於 GIF 和 8 位元 PNG 影像,我可以把它放在背景圖上面,而不必擔心邊緣的顏色。</p>
<p>我改用不同的方式載入影像。我直接在 HTML 文件上放置影像,並使用 CSS 規則隱藏影像(<code>display:none</code>)。我給兩個影像各指定一個 <code>id</code> 屬性,以便選取。Script 本身很簡單。我先在 canvas 上畫出切下並縮小過的影像(第一個 <code>drawImage</code> 語句),然後加上畫框(第二個 <code>drawImage</code> 語句)。</p>
<p><a class="external" href="/samples/canvas-tutorial/3_3_canvas_drawimage.html" title="samples/canvas-tutorial/3_3_canvas_drawimage.html">檢視範例</a> <img align="right" alt="Source image" class="internal" src="/@api/deki/files/93/=Canvas_picture_frame.png"></p>
<pre class="brush: js">function draw() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  // 繪製切片
  ctx.drawImage(document.getElementById('source'),
                33,71,104,124,21,20,87,104);

  // 繪製畫框
  ctx.drawImage(document.getElementById('frame'),0,0);
}
</pre>
<h2 name="Art_gallery_example">藝術圖庫範例</h2>
<p><img align="right" alt="" class="internal" src="/@api/deki/files/57/=Canvas_art_gallery.jpg">在本章的最後一個範例裡,我會製作一些藝術圖庫。圖庫以包含若干圖像的表格組合。當頁面載入完畢時,就在頁面上的每一個圖像之前插入加上畫框的 canvas 元素。</p>
<p>在本例中,所有的圖像都有固定的寬和高,並在邊緣加上畫框。你可以修改 Script,使畫框配合圖像的大小。</p>
<p>以下的代碼應該很容易看懂。迴圈遍歷陣列裡的所有圖像,並為圖像加上新的 canvas 元素。對於那些不熟悉 DOM 的人來說,可能還有一處需要注意,那就是 <a href="/En/DOM/Node.insertBefore" title="en/DOM/element.insertBefore">insertBefore</a> 方法的使用<span style="font-family: monospace;">。</span><code>insertBefore</code> 是圖像元素的親節點(表格)的方法,我們打算在圖像元素前面插入新的結點(canvas 元素)。</p>
<p><a class="external" href="/samples/canvas-tutorial/3_4_canvas_gallery.html" title="samples/canvas-tutorial/3_4_canvas_gallery.html">檢視範例</a></p>
<pre class="brush: js">function draw() {

  // 遍歷所有圖像的迴圈
  for (i=0;i&lt;document.images.length;i++){

    // 無須為畫框圖像加上 canvas
    if (document.images[i].getAttribute('id')!='frame'){

      // 建立 canvas 元素
      canvas = document.createElement('CANVAS');
      canvas.setAttribute('width',132);
      canvas.setAttribute('height',150);

      // 在圖像之前插入
      document.images[i].parentNode.insertBefore(canvas,document.images[i]);

      ctx = canvas.getContext('2d');

      // 把圖像畫在 canvas 上
      ctx.drawImage(document.images[i],15,20);

      // 加上畫框
      ctx.drawImage(document.getElementById('frame'),0,0);
    }
  }
}
</pre>
<p>{{ PreviousNext("Canvas 教學:繪製圖形", "Canvas 教學:套用樣式和色彩") }}</p>
<p>{{ languages( { "en": "en/Canvas_tutorial/Using_images", "fr": "fr/Tutoriel_canvas/Utilisation_d\'images", "ja": "ja/Canvas_tutorial/Using_images", "pl": "pl/Przewodnik_po_canvas/Zastosowanie_obrazk\u00f3w", "zh-cn": "cn/Canvas_tutorial/Using_images", "ru": "ru/\u041e\u0431\u0443\u0447\u0435\u043d\u0438\u0435_canvas/\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435_\u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a" } ) }}</p>
Revert to this revision