使用影像

  • 版本網址代稱: Canvas_教學/使用影像
  • 版本標題: 使用影像
  • 版本 ID: 274205
  • 建立日期:
  • 建立者: happysadman
  • 是目前線上的版本?
  • 回應 no wording changes

版本內容

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';
  }

縮放

The second variant of the drawImage method adds two new parameters and it allows us to place scaled images on the canvas.

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

Where width and height is the image's size on the target canvas.

drawImage 範例 2

In this example I'm going to use an image as a wallpaper and repeat it several times on the canvas. This is done simply by looping and placing the scaled images at different positions. In the code below the first for loops through the rows the second for loop the columns. The image is scaled one third of its original size which is 50x38 pixels. We'll see how this could also have been achieved, by creating a custom pattern, later in this tutorial.

Note: Images can become blurry when scaling up or grainy if they're scaled down too much. Scaling is probably best not done if you've got some text in it which needs to remain legible.

View this example 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';
  }

切片

The third and last variant of the drawImage method has eight new parameters. We can use this method to slice parts of a source image and draw them to the canvas.

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

The first parameter image, just as with the other variants, is either a reference to an image object or a reference to a different canvas element. For the other eight parametes it's best to look at the image on the right. The first four parameters define the location and size of the slice on the source image. The last four parameters define the position and size on the destination canvas.

Slicing can be a useful tool when you want to make compositions. You could have all elements in a single image file and use this method to composite a complete drawing. For instance, if you want to make a chart you could have a PNG image containing all the necessary text in a single file and depending on your data could change the scale of your chart without very much diffculty. Another advantage is that you don't need to load every image individually.

drawImage 範例 3

In this example I'm going to use the same rhino as we've seen above, but now I'm going to slice its head out and composite it into a picture frame. The image of the picture frame includes a dropshadow which has been saved as a 24-bit PNG image. Because 24-bit PNG images include a full 8-bit alpha channel, unlike GIF and 8-bit PNG images, I can place it onto any background and don't have to worry about a matte color.

I took a different approach to the loading of the images than the example above. I just placed the images directly in my HTML document and used a CSS rule to hide them from view (display:none). I assigned both images an id attribute to make them easier to select. The script itself is very simple. I first draw the sliced and scaled image on the canvas (first drawImage statement), and then place the frame on top (second drawImage statement).

View this example Source image

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);
}

藝術圖庫範例

In the final example of this chapter I've made a little art gallery. The gallery consists of a table containing several images. When the page is loaded, for each image in the page a canvas element is inserted and a frame is drawn arround it.

In my case, all images have a fixed width and height, and so does the frame that's drawn around it. You could enhance the script so that it uses the image's width and height to make the frame fit perfectly around it.

The code below should be self-explanatory. We loop through the images array and add new canvas elements accordingly. Probably the only thing to note, for those not so familar with the DOM, is the use of the insertBefore method. insertBefore is a method of the parent node (a table cell) of the element (the image) before which we want to insert our new node (the canvas element).

View this example

function draw() {

  // Loop through all images
  for (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);
    }
  }
}

{{ PreviousNext("Canvas tutorial:Drawing shapes", "Canvas tutorial:Applying styles and colors") }}

{{ 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>The second variant of the <code>drawImage</code> method adds two new parameters and it allows us to place scaled images on the 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;">
<p><code><strong>drawImage</strong>(image, x, y, width, height)</code></p>
</div>
<p>Where <code>width</code> and <code>height</code> is the image's size on the target 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"> In this example I'm going to use an image as a wallpaper and repeat it several times on the canvas. This is done simply by looping and placing the scaled images at different positions. In the code below the first <code>for</code> loops through the rows the second <code>for</code> loop the columns. The image is scaled one third of its original size which is 50x38 pixels. We'll see how this could also have been achieved, by creating a custom pattern, later in this tutorial.</p>
<p><strong>Note</strong>: Images can become blurry when scaling up or grainy if they're scaled down too much. Scaling is probably best not done if you've got some text in it which needs to remain legible.</p>
<p><a class="external" href="/samples/canvas-tutorial/3_2_canvas_drawimage.html" title="samples/canvas-tutorial/3_2_canvas_drawimage.html">View this example</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>The third and last variant of the <code>drawImage</code> method has eight new parameters. We can use this method to slice parts of a source image and draw them to the 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">The first parameter <code>image</code>, just as with the other variants, is either a reference to an image object or a reference to a different canvas element. For the other eight parametes it's best to look at the image on the right. The first four parameters define the location and size of the slice on the source image. The last four parameters define the position and size on the destination canvas.</p>
<p>Slicing can be a useful tool when you want to make compositions. You could have all elements in a single image file and use this method to composite a complete drawing. For instance, if you want to make a chart you could have a PNG image containing all the necessary text in a single file and depending on your data could change the scale of your chart without very much diffculty. Another advantage is that you don't need to load every image individually.</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">In this example I'm going to use the same rhino as we've seen above, but now I'm going to slice its head out and composite it into a picture frame. The image of the picture frame includes a dropshadow which has been saved as a 24-bit PNG image. Because 24-bit PNG images include a full 8-bit alpha channel, unlike GIF and 8-bit PNG images, I can place it onto any background and don't have to worry about a matte color.</p>
<p>I took a different approach to the loading of the images than the example above. I just placed the images directly in my HTML document and used a CSS rule to hide them from view (<code>display:none</code>). I assigned both images an <code>id</code> attribute to make them easier to select. The script itself is very simple. I first draw the sliced and scaled image on the canvas (first <code>drawImage</code> statement), and then place the frame on top (second <code>drawImage</code> statement).</p>
<p><a class="external" href="/samples/canvas-tutorial/3_3_canvas_drawimage.html" title="samples/canvas-tutorial/3_3_canvas_drawimage.html">View this example</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');

  // 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>
<h2 name="Art_gallery_example">藝術圖庫範例</h2>
<p><img align="right" alt="" class="internal" src="/@api/deki/files/57/=Canvas_art_gallery.jpg">In the final example of this chapter I've made a little art gallery. The gallery consists of a table containing several images. When the page is loaded, for each image in the page a canvas element is inserted and a frame is drawn arround it.</p>
<p>In my case, all images have a fixed width and height, and so does the frame that's drawn around it. You could enhance the script so that it uses the image's width and height to make the frame fit perfectly around it.</p>
<p>The code below should be self-explanatory. We loop through the images array and add new canvas elements accordingly. Probably the only thing to note, for those not so familar with the DOM, is the use of the <a href="/En/DOM/Node.insertBefore" title="en/DOM/element.insertBefore">insertBefore</a> method. <code>insertBefore</code> is a method of the parent node (a table cell) of the element (the image) before which we want to insert our new node (the canvas element).</p>
<p><a class="external" href="/samples/canvas-tutorial/3_4_canvas_gallery.html" title="samples/canvas-tutorial/3_4_canvas_gallery.html">View this example</a></p>
<pre class="brush: js">function draw() {

  // Loop through all images
  for (i=0;i&lt;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>{{ PreviousNext("Canvas tutorial:Drawing shapes", "Canvas tutorial:Applying styles and colors") }}</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>
還原至此版本