使用影像

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

版本內容

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

匯入影像

Importing images is basically a two step process:

  • Firstly we need a reference to a JavaScript Image object or other canvas element as a source. It isn't possible to use images by simply providing a URL/path to them.
  • Secondly we draw the image on the canvas using the drawImage function.

Let's look at step one first. There are basically four options available:

使用同一頁面上的影像

We can access all images on a page by using either the document.images collection, the document.getElementsByTagName method, or if we know the ID attribute of the image, the document.getElementById method.

使用其他的 canvas 元件

Just as with normal images we access other canvas elements using either the document.getElementsByTagName method or the document.getElementById method. Make sure you've drawn something to the source canvas before using it in your target canvas.

One of the more practical uses of this would be to use a second canvas element as a thumbnail view of the other larger canvas.

從頭建立影像

Another option is to create new Image objects in our script. The main disadvantage of this approach is that if we don't want our script to halt in the middle because it needs to wait for an image to load, we need some form of image preloading.

Basically to create a new image object we do this:

var img = new Image();   // Create new Image object
img.src = 'myImage.png'; // Set source path

When this script gets executed, the image starts loading. If loading isn't finished when a drawImage statement gets executed, the script halts until the image is finished loading. If you don't want this to happen, use an onload event handler:

var img = new Image();   // Create new Image object
img.onload = function(){
  // execute drawImage statements here
}
img.src = 'myImage.png'; // Set source path

If you're only using one external image this can be a good approach but once you need to track more than one we need to resort to something more cunning. It's beyond the scope of this tutorial to look at image preloading tactics but you can check out JavaScript Image Preloader for a complete solution.

使用 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( { "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>Importing images is basically a two step process:</p>
<ul> <li>Firstly we need a reference to a JavaScript Image object or other canvas element as a source. It isn't possible to use images by simply providing a URL/path to them.</li> <li>Secondly we draw the image on the canvas using the <code>drawImage</code> function.</li>
</ul>
<p>Let's look at step one first. There are basically four options available:</p>
<h4 name="Using_images_which_are_on_the_same_page">使用同一頁面上的影像</h4>
<p>We can access all images on a page by using either the <code><a href="/en/DOM/document.images" title="en/DOM/document.images">document.images</a></code> collection, the <code><a href="/en/DOM/document.getElementsByTagName" title="en/DOM/document.getElementsByTagName">document.getElementsByTagName</a></code> method, or if we know the ID attribute of the image, the <code><a href="/en/DOM/document.getElementById" title="en/DOM/document.getElementById">document.getElementById</a></code> method.</p>
<h4 name="Using_other_canvas_elements">使用其他的 canvas 元件</h4>
<p>Just as with normal images we access other canvas elements using either the <code><a href="/en/DOM/document.getElementsByTagName" title="en/DOM/document.getElementsByTagName">document.getElementsByTagName</a></code> method or the <code><a href="/en/DOM/document.getElementById" title="en/DOM/document.getElementById">document.getElementById</a></code> method. Make sure you've drawn something to the source canvas before using it in your target canvas.</p>
<p>One of the more practical uses of this would be to use a second canvas element as a thumbnail view of the other larger canvas.</p>
<h4 name="Creating_an_image_from_scratch">從頭建立影像</h4>
<p>Another option is to create new <code>Image</code> objects in our script. The main disadvantage of this approach is that if we don't want our script to halt in the middle because it needs to wait for an image to load, we need some form of image preloading.</p>
<p>Basically to create a new image object we do this:</p>
<pre class="brush: js">var img = new Image();   // Create new Image object
img.src = 'myImage.png'; // Set source path
</pre>
<p>When this script gets executed, the image starts loading. If loading isn't finished when a <code>drawImage</code> statement gets executed, the script halts until the image is finished loading. If you don't want this to happen, use an <code>onload</code> event handler:</p>
<pre class="brush: js">var img = new Image();   // Create new Image object
img.onload = function(){
  // execute drawImage statements here
}
img.src = 'myImage.png'; // Set source path
</pre>
<p>If you're only using one external image this can be a good approach but once you need to track more than one we need to resort to something more cunning. It's beyond the scope of this tutorial to look at image preloading tactics but you can check out <a class="external" href="http://www.webreference.com/programming/javascript/gr/column3/">JavaScript Image Preloader</a> for a complete solution.</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 class="brush: js">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 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( { "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