使用影像

  • 版本網址代稱: Canvas_教學/使用影像
  • 版本標題: 使用影像
  • 版本 ID: 274201
  • 建立日期:
  • 建立者: 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 嵌入影像

Another possible way to include images is via the data: url. Data urls allow you to completely define an image as a Base64 encoded string of characters directly in your code. One advantage of data urls is that the resulting image is available immediately without another round trip to the server. ( Another advantage is that it is then possible to encapsulate in one file all of your CSS, Javascript, HTML, and images, making it more portable to other locations. ) Some disadvantages of this method are that your image is not cached, and for larger images the encoded url can become quite long:

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

drawImage

Once we have a reference to our source image object we can use the drawImage method to render it to the canvas. As we we'll see later the drawImage method is overloaded and has three different variants. In its most basic form it looks like this.

drawImage(image, x, y)

Where image is a reference to our image or canvas object. x and y form the coordinate on the target canvas where our image should be placed.

drawImage 範例 1

In the following example I will be using an external image as the backdrop of a small line graph. Using backdrops can make your script considerably smaller because we don't need to draw an elaborate background. I'm only using one image here so I use the image object's onload event handler to execute the drawing statements. The drawImage method places the backdrop on the coordinate (0,0) which is the top left corner of the canvas.

View this example

  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>var img = new Image();   // 建立新的 image 物件
img.src = 'myImage.png'; // 設定來源路徑
</pre>
<p>當 script 執行時,開始載入影像。如果未能在執行 <code>drawImage</code> 之前載入完成,script 會停住,直到影像載入完成。如果你不希望這樣的話,使用 <code>onload</code> 事件處理器︰</p>
<pre>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>Another possible way to include images is via the <a class="external" href="http://en.wikipedia.org/wiki/Data:_URL">data: url</a>. Data urls allow you to completely define an image as a Base64 encoded string of characters directly in your code. One advantage of data urls is that the resulting image is available immediately without another round trip to the server. ( Another advantage is that it is then possible to encapsulate in one file all of your CSS, Javascript, HTML, and images, making it more portable to other locations. ) Some disadvantages of this method are that your image is not cached, and for larger images the encoded url can become quite long:</p>
<pre>var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
</pre><h2 name="drawImage"><code>drawImage</code></h2>
<p>Once we have a reference to our source image object we can use the <code>drawImage</code> method to render it to the canvas. As we we'll see later the <code>drawImage</code> method is overloaded and has three different variants. In its most basic form it looks like this.</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>Where <code>image</code> is a reference to our image or canvas object. <code>x</code> and <code>y</code> form the coordinate on the target canvas where our image should be placed.</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">In the following example I will be using an external image as the backdrop of a small line graph. Using backdrops can make your script considerably smaller because we don't need to draw an elaborate background. I'm only using one image here so I use the image object's <code>onload</code> event handler to execute the drawing statements. The <code>drawImage</code> method places the backdrop on the coordinate (0,0) which is the top left corner of the canvas.</p>
<p><a class="external" href="/samples/canvas-tutorial/3_1_canvas_drawimage.html" title="samples/canvas-tutorial/3_1_canvas_drawimage.html">View this example</a></p>
<pre>  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>  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>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>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>
Revert to this revision