Canvas教程(4):Using images应用图像


应用图像 Using images

One of the more fun features of the canvas is the abillity to use images. These can be used to do dynamic photo compositing or used as backdrops of graphs etc. It's currently also the only way to add text to them (The specification does not contain any functions to draw text). External images can be used in any format supported by Gecko (e.g. PNG, GIF or JPEG format). Other canvas elements on the same page can also be used as the source.

Canvas 相当有趣的一项功能就是可以引入图像,它可以用于图片合成或者制作背景等。而目前仅可以在图像中加入文字(标准说明中并没有包含绘制文字的功能)。只要是 Gecko 支持的图像(如 PNG,GIF,JPEG等)都可以引入到 canvas 中,而且其它的 canvas 元素也可以作为图像的来源。

引入图像 Importing images

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.
  • 第一当然是来源图片,不是简单的 URL 路径,但可以是一个 JavaScript 的 Image 对象引用,又或者其它的 canvas 元素。
  • 然后用 drawImage 方法将图像插入到 canvas 中。

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

先来看看第一步,基本上有四种可选方式:

引用页面内的图片 Using images which are on the same page

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.

我们可以通过 document.images 集合、document.getElementsByTagName 方法又或者 document.getElementById 方法来获取页面内的图片(如果已知图片元素的 ID。

使用其它 canvas 元素 Using other canvas elements

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.

和引用页面内的图片类似地,用 document.getElementsByTagName document.getElementById 方法来获取其它 canvas 元素。但你引入的应该是已经准备好的 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.

一个常用的应用就是为另一个大的 canvas 做缩略图。

<

由零开始创建图像 Creating an image from scratch

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.

另外,我们可以用脚本创建一个新的 Image 对象,但这种方法的主要缺点是如果不希望脚本因为等待图片装置而暂停,还得需要突破预装载。

Basically to create a new image object we do this:

我们可以通过下面简单的方法来创建图片:

  1. var img = new Image();   // Create new Image object   
  2. img.src = 'myImage.png'// Set source path  
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:

当脚本执行后,图片开始装载。若调用 drawImage 时,图片没装载完,脚本会等待直至装载完毕。如果不希望这样,可以使用 onload 事件:

  1. var img = new Image();   // Create new Image object   
  2. img.onload = function(){   
  3.   // execute drawImage statements here   
  4. }   
  5. img.src = 'myImage.png'// Set source path  
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.

如果你只用到一张图片的话,这已经够了。但一旦需要不止一张图片,那就需要更加复杂的处理方法,但图片预装载策略超出本教程的范围,感兴趣的话可以参考JavaScript Image Preloader。

通过 data: url 方式嵌入图像 Embedding an image via 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:

我们还可以通过 data: url 方式来引用图像。Data urls 允许用一串 Base64 编码的字符串的方式来定义一个图片。其优点就是图片内容即时可用,无须再到服务器兜一圈。(还有一个优点是,可以将 CSS,JavaScript,HTML 和 图片全部封装在一起,迁移起来十分方便。)缺点就是图像没法缓存,图片大的话内嵌的 url 数据会相当的长:

  1. var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';  
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 方法将它渲染到 canvas 里。drawImage 方法有三种形态,下面是最基础的一种。

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.

其中 image 是 image 或者 canvas 对象,xy 是其在目标 canvas 里的起始坐标。

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.

下面一个例子我用一个外部图像作为一线性图的背景。用背景图我们就不需要绘制负责的背景,省下不少代码。这里只用到一个 image 对象,于是就在它的 onload 事件响应函数中触发绘制动作。drawImage 方法将背景图放置在 canvas 的左上角 (0,0) 处。

function draw() {   

  1.     var ctx = document.getElementById('canvas').getContext('2d');   
  2.     var img = new Image();   
  3.     img.onload = function(){   
  4.       ctx.drawImage(img,0,0);   
  5.       ctx.beginPath();   
  6.       ctx.moveTo(30,96);   
  7.       ctx.lineTo(70,66);   
  8.       ctx.lineTo(103,76);   
  9.       ctx.lineTo(170,15);   
  10.       ctx.stroke();   
  11.     }   
  12.     img.src = 'images/backdrop.png';   
  13.   }  
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';
  }

缩放 Scaling

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

drawImage 方法的又一变种是增加了两个用于控制图像在 canvas 中缩放的参数。

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

Where width and height is the image's size on the target canvas. 当中 widthheight 分别是图像在 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.

在这个例子里,我会用一张图片像背景一样在 canvas 中以重复平铺开来。实现起来也很简单,只需要循环铺开经过缩放的图片即可。见下面的代码,第一层 for 循环是做行重复,第二层是做列重复的。图像大小被缩放至原来的三分之一,50x38 px。这种方法可以用来很好的达到背景图案的效果,在下面的教程中会看到。

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.

注意:图像可能会因为大幅度的缩放而变得起杂点或者模糊。如果您的图像里面有文字,那么最好还是不要进行缩放,因为那样处理之后很可能图像里的文字就会变得无法辨认了。

function draw() {   

  1.     var ctx = document.getElementById('canvas').getContext('2d');   
  2.     var img = new Image();   
  3.     img.onload = function(){   
  4.       for (i=0;i<4;i++){   
  5.         for (j=0;j<3;j++){   
  6.           ctx.drawImage(img,j*50,i*38,50,38);   
  7.         }   
  8.       }   
  9.     }   
  10.     img.src = 'images/rhino.jpg';   
  11.   }  
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';
  }

切片 Slicing

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 方法的第三个也是最后一个变种有8个新参数,用于控制做切片显示的。

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.

第一个参数和其它的是相同的,都是一个图像或者另一个 canvas 的引用。其它8个参数最好是参照右边的图解,前4个是定义图像源的切片位置和大小,后4个则是定义切片的目标显示位置和大小。

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.

切片是个做图像合成的强大工具。假设有一张包含了所有元素的图像,那么你可以用这个方法来合成一个完整图像。例如,你想画一张图表,而手上有一个包含所有必需的文字的 PNG 文件,那么你可以很轻易的根据实际数据的需要来改变最终显示的图表。这方法的另一个好处就是你不需要单独装载每一个图像。

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.

在这个例子里面我用到上面已经用过的犀牛图像,不过这次我要给犀牛头做个切片特写,然后合成到一个相框里面去。相框带有阴影效果,是一个以 24-bit PNG 格式保存的图像。因为 24-bit PNG 图像带有一个完整的 8-bit alpha 通道,与 GIF 和 8-bit PNG 不同,我可以将它放成背景而不必担心底色的问题。

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).

我用一个与上面用到的不同的方法来装载图像,直接将图像插入到 HTML 里面,然后通过 CSS 隐藏(display:none)它。两个图像我都赋了 id ,方便后面使用。看下面的脚本,相当简单,首先对犀牛头做好切片(第一个 drawImage )放在 canvas 上,然后再上面套个相框(第二个 drawImage )。

  1. function draw() {   
  2.   var canvas = document.getElementById('canvas');   
  3.   var ctx = canvas.getContext('2d');   
  4.   
  5.   // Draw slice   
  6.   ctx.drawImage(document.getElementById('source'),   
  7.                 33,71,104,124,21,20,87,104);   
  8.   
  9.   // Draw frame   
  10.   ctx.drawImage(document.getElementById('frame'),0,0);   
  11. }  
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);
}

Art gallery example

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).

  1. function draw() {   
  2.   
  3.   // Loop through all images   
  4.   for (i=0;i<document.images.length;i++){   
  5.   
  6.     // Don't add a canvas for the frame image   
  7.     if (document.images[i].getAttribute('id')!='frame'){   
  8.   
  9.       // Create canvas element   
  10.       canvas = document.createElement('CANVAS');   
  11.       canvas.setAttribute('width',132);   
  12.       canvas.setAttribute('height',150);   
  13.   
  14.       // Insert before the image   
  15.       document.images[i].parentNode.insertBefore(canvas,document.images[i]);   
  16.   
  17.       ctx = canvas.getContext('2d');   
  18.   
  19.       // Draw image to canvas   
  20.       ctx.drawImage(document.images[i],15,20);   
  21.   
  22.       // Add frame   
  23.       ctx.drawImage(document.getElementById('frame'),0,0);   
  24.     }   
  25.   }   
  26. }  

« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3