This is the third in a series of canvas notes to study and review. See Canvas Core Technologies for the full notes

Through the learning of canvas core technology – how to draw graphics, we know how to draw arbitrary polygons and the filling rules of pictures. Drawing pictures and text are also widely used in Canvas. In this article, we will talk about drawing images and text in detail.

The picture

In Canvas, we can draw an image directly to the canvas, just like using the IMG tag, except that the image is drawn to the canvas instead of a separate HTML element. Canvas provides drawImage method to draw pictures. This method can be used in three forms, as follows:

  • void drawImage(image,dx,dy);Draw the image directly to the specified Canvas coordinate, the image is passed in by image, the coordinates are passed in by dx and dy.
  • void drawImage(image,dx,dy,dw,dh);Same as above, but specify the width and height to draw the image. The width and height are passed in by DW and DH.
  • void drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh);This is the most complex, the most flexible forms of use, the first parameter is for drawing elements, the second to the fifth parameter, specify the coordinates of the original image and width is high, this area will be drawn to the canvas, and other area is ignored, the last four parameters like form 2, specifies the canvas target coordinates in the high and wide.

According to the number of parameters, we will call different forms of drawImage. The first form is the simplest, which is to draw the original image directly to the specified coordinates of the target canvas. The width and height of the image are the width and height of the original image without scaling. In the second form, the width and height of the drawing area of the target canvas are specified. Then the width and height of the image drawn on the canvas is fixed and the image will be scaled. If the specified DW and DH are not equal to the width and height of the original image, the image will be compressed or stretched out of shape. The third form specifies the region of the original picture to be drawn and the region in the target canvas respectively. By sx, SY, SW and sh, we can select only a part of the original picture or specify the complete picture. By dx, dy, Dw and DH, we can specify the target canvas region to be drawn.

let img = document.createElement('img'); // Create the img element
img.src = './learn9/google.png'; // specify img SRC
img.addEventListener(
  'load',
  () => {
    ctx.drawImage(img, 0.0); // Draw the img element with drawImage(img,dx,dy)
  },
  false,);Copy the code

In the example above, the original size of the Google image is 544*184, while the canvas area is 300*150 by default. We call the first form, draw the picture directly to the origin of the canvas coordinates, the picture is not scaled, beyond the canvas area, the excess part will be ignored by canvas. One thing to note is that I started drawing in the onload event of the image, because drawing an image without loading it is useless. In the following code examples, I will post only the code in the onload event, and omit the image load part of the code, which is the same.

let canvasWidth = canvas.width; // Get the canvas width
let canvasHeight = canvas.height; // Get the canvas height
ctx.drawImage(img, 0.0, canvasWidth, canvasHeight); 
Copy the code

We set the target canvas area as the width and height of the canvas, so the picture will always be drawn in the whole canvas, and it can also be seen that the drawn picture is distorted. We can calculate the width of the canvas target area according to the width of the canvas target area by calculating the aspect ratio of the original picture, or calculate the width of the Canvas target area according to the height of the canvas target area.

let imgWidth = img.width; // Get the width of the image
let imgHeight = img.height; // Get the height of the image
let targetWidth = canvasWidth; // Specify the width of the target canvas area
let targetHeight = (imgHeight * targetWidth) / imgWidth; // Calculate the height of the target canvas area
ctx.drawImage(img, 0.0, targetWidth, targetHeight);
Copy the code

As can be seen from the figure, the target canvas area is calculated according to the image aspect ratio. Finally, the effect drawn by the picture is equal scale without deformation.

Let’s look at the most complex and flexible third approach. In this way, we can draw the red o in the Google image.

ctx.drawImage(img, 143.48.90.90.0.0.90.90);
Copy the code

In the Google picture, the coordinate of the red letter o in the original picture is (143,48), and its width and height is 90*90. We simply draw this letter on the coordinate of (0,0) of canvas, and its width and height is also 90*90. You can make it more complicated. Make the red letter O the same height as the canvas, enlarge its width equally, and the center of the circle is right in the center of the canvas. The implementation is as follows:

let oWidth = 90; // Get the width of the letter O
let oHeight = 90; // Get the height of the letter O
let targetHeight = canvas.height; // Specify the height of the target canvas area
let targetWidth = (oWidth * targetHeight) / oHeight; // Calculate the width of the target canvas area
let targetX = (canvas.width - targetWidth) / 2; // Move the target canvas coordinate X
ctx.drawImage(img, 143.48, oWidth, oHeight, targetX, 0, targetWidth, targetHeight);
Copy the code

The first parameter returned by drawImage, image, not only can be an image element, but can actually be a canavs element, a video element. The common use of off-screen canvas is to draw the canvas invisible off-screen to the canvas of the current display screen. The off-screen canvas part will be covered later in the game, but I won’t go into details here.

The image pixel

GetImageData, putImageData, createImageData. These functions can directly change the value of a specific pixel in the image, so that you can do some operations on the image, such as filter.

ImgData = ctx.getImageData(sx,sy,sw,sh). GetImageData (sx,sy,sw,sh). GetImageData (sx,sy). The width and height are sw and sh, and its return value is an ImageData object with properties like width, height, and data.

  • Imagedata.width, an unsigned long integer, represents the width of the pixels in the image region.

  • Imagedata. height, an unsigned long integer that represents the height of the pixels in the image region.

  • Imagedata. data, a Uint8ClampedArray array with 4 units in it, representing a pixel value. An image value is represented by RGBA. These 4 units represent R, G, B, and A respectively. The representation means red, green, blue, and transparency, ranging from 0 to 255.

Note that if we call ctx.getimageData (sx, SY,sw,sh) and the rectangle is outside the canvas area, the excess will be represented by a black RGBA value with transparency of 0, i.e. (0,0,0).

let imgWidth = img.width; // Get the width of the image
let imgHeight = img.height; // Get the height of the image
let targetWidth = canvasWidth; // Specify the width of the target canvas area
let targetHeight = (imgHeight * targetWidth) / imgWidth; // Calculate the height of the target canvas area
ctx.drawImage(img, 0.0, targetWidth, targetHeight);
let imgData = ctx.getImageData(0.0, canvasWidth, canvasHeight);
console.log(`canvas.width = ${canvasWidth}`);
console.log(`canvas.height = ${canvasHeight}`);
console.log(imgData);
Copy the code

It can be seen that the default width and height of our canvas is 300*150. The pixel data value of the entire Canvas area is obtained through CTx. getImageData, and the pixel width and height of the ImageData device obtained is also 300*150. Imagedata.data array length is 180000, this is because the imgData pixel number is 300*150, and each pixel is represented by 4 components, so 300*150*4 = 180000.

After we get the pixel data of a rectangle area of canvas through getImageData, we can change the color component value in the imageData.data array, and then draw the changed imageData to canvas through putImageData method. PutImageData can be used in two call forms, as follows,

  • ctx.putImageData(imgData,dx,dy)In this way, imgData is drawn to the canvas region (dx,dy) coordinates. The rectangle size drawn to canvas is the size of imgData’s rectangle.
  • ctx.putImageData(imgData,dx,dy,dirtyX,dirtyY,dirtyW,dirtyH)ImgData dirty data area (dirtyX,dirtyY) and width and height dirtyW, dirtyH. In this form, you can only draw a certain area of imgData onto the canvas.
let canvasWidth = canvas.width;
let canvasHeight = canvas.height;
let img = document.createElement('img');
img.src = './learn9/google.png';
img.addEventListener(
 'load', () = > {let imgWidth = img.width; // Get the width of the image
   let imgHeight = img.height; // Get the height of the image
   let targetWidth = canvasWidth; // Specify the width of the target canvas area
   let targetHeight = (imgHeight * targetWidth) / imgWidth; // Calculate the height of the target canvas area
   ctx.drawImage(img, 0.0, targetWidth, targetHeight);
   // Manipulate ImageData pixel data
   let imgData = ctx.getImageData(0.0, canvasWidth, canvasHeight);
   oprImageData(imgData, (r, g, b, a) => {
     if (a === 0) {
       return [r, g, b, 255]; // Change the transparent black pixel value to opaque
     }
     return [r, g, b, a];
   });
   // Draw imgData to the center of the canvas. Anything outside the Canvas area is automatically ignored
   ctx.putImageData(imgData, canvasWidth / 2, canvasHeight / 2);
 },
 false,);// Iterate over the pixel data
function oprImageData(imgData, oprFunction) {
 let data = imgData.data;
 for (let i = 0, l = data.length; i < l; i = i + 4) {
   let pixel = oprFunction(data[i], data[i + 1], data[i + 2], data[i + 3]);
   data[i] = pixel[0];
   data[i + 1] = pixel[1];
   data[i + 2] = pixel[2];
   data[i + 3] = pixel[3]; }}Copy the code

Above, we iterate through the Data array in ImageData and change the transparency of the zero pixel value to 1 (255/255=1). When traversing the array of pixels, we increase the value of I by 4 each time. This is because A pixel value is represented by four array elements, namely R, G, B and A. We can change only one component of A pixel value, such as transparency.

ctx.putImageData(imgData, canvasWidth / 2, canvasHeight / 2.79.27.50.50);
Copy the code

By specifying the dirty data area in ImageData, we draw only the red letter O and ignore the rest. Before calling putImageData above, we changed the transparency of part of the pixel values by traversing the pixel data. This way of manipulating pixel values is very useful in image processing and other fields, such as common image grayscale and inverting color.

// Manipulate ImageData pixel data
let imgData = ctx.getImageData(0.0, canvasWidth, canvasHeight);
ctx.clearRect(0.0, canvasWidth, canvasHeight); / / remove the canvas
oprImageData(imgData, (r, g, b, a) => {
    return [255 - r, 255 - g, 255 - b, a]; // Invert color
});
ctx.putImageData(imgData, 0.0);
Copy the code

Subtract the original color component from the RGB value of the color component 255, and you can see that the color of each letter in Google is different from the original color. This is changing the value of each color component, using illogical calculation, you can get a different image after processing.

// Manipulate ImageData pixel data
let imgData = ctx.getImageData(0.0, canvasWidth, canvasHeight);
ctx.clearRect(0.0, canvasWidth, canvasHeight); / / remove the canvas
oprImageData(imgData, (r, g, b, a) => {
    let avg = (r + g + b) / 3;
    return [avg, avg, avg, a]; / / gray
});
ctx.putImageData(imgData, 0.0);
Copy the code

By taking the average value of RGB, every letter of the original picture is gray. Of course, when calculating, you can add a coefficient to each component, for example, the formula let AVG = 0.299r + 0.587g + 0.114b. For specific application, you can see Grayscale.

Finally, let’s look at createImageData, which is pretty easy to understand, which is to create an ImageData object in two forms, as follows,

  • ctx.createImageData(width,height), you can specify the width and height to create oneImageDataObject,ImageData.dataThe pixel values are a transparent black, i.e. (0,0,0,0).
  • ctx.createImageData(imgData), you can specify an existingImageDataObject to create a new oneImageDataObject, newly createdImageDataThe width and height of the object and the parameterImageDataIs the same width and height, but the pixel value is different, newly createdImageDataThe pixel values of are transparent black, i.e. (0,0,0,0).

The text

In canvas, we can not only draw graphics, pictures, but also draw text. It is easier to draw text. First, set the text style of the current CTX brush, such as the font size, font style, the way to it, and so on, similar to CSS.

There are three methods associated with text, as follows,

  • strokeText(text,x,y,maxWidth?)Draws the specified text as a stroke, which also specifies the draw coordinates (x, y), and one last optional argument, the maximum width if the drawn text exceeds the specifiedmaxWidth, the text will be drawn to the maximum width, the space between the text will be reduced, the text may be compressed.
  • fillText(text,x,y,maxWidth?)And the samestrokeTextThe same, except that the text is drawn as a fill, and the parameters have the same meaning.
  • measureText(text)In the current text style, measures the width of the drawn text and returns an object that has onewidthProperties. The main thing to note is that the text style must be set before the measurement is accurate.

The property Settings directly related to the text are as follows,

  • fontAs in CSS, you can specify text font size, font set, font style, etc. But in the canvas,line-heightIs forcibly set tonormal, ignoring other Settings.
  • textAlign, set the level of the text to its mode, the optional values are:left.right.center.start.end. The default value isstart. See Synonyms at meaningsTextAlign values.
  • textBaselineTo set the vertical alignment of the text. The possible values are:top.hanging.middle.alphabetic.ideographic.bottom. The default value isalphabetic. See Synonyms at meaningsTextBaseline values.

Of course, there are other attributes that affect the final rendering of the text, such as adding a shadow to the current CTX, or setting the fillStyle to be a picture or gradient. These are global property Settings that affect all other drawings on the canvas, not just text, so I won’t go into detail here.

let textAligns = ['left'.'right'.'center'.'start'.'end']; / / textAlign values
let colors = ['red'.'blue'.'green'.'orange'.'blueviolet']; // Stroke color
ctx.font = '18px sans-serif'; / / set the font
for (let [index, textAlign] of textAligns.entries()) {
  ctx.save();
  ctx.textAlign = textAlign; / / set textAlign
  ctx.strokeStyle = colors[index]; // Set the stroke color
  ctx.strokeText(textAlign, width / 2.20 + index * 30); // Use stroke to draw text
  ctx.restore();
}
Copy the code

Start is the same as left and end is the same as right. If you start from left to right, start is the same as left. If you start from right to left, start is the same as right.

let textBaselines = ['top'.'hanging'.'middle'.'alphabetic'.'ideographic'.'bottom'];
let colors = ['red'.'blue'.'green'.'orange'.'blueviolet'.'cyan']; // Stroke color
ctx.font = '18px sans-serif'; / / set the font
for (let [index, textBaseline] of textBaselines.entries()) {
  ctx.save();
  ctx.textBaseline = textBaseline; / / set textBaseline
  ctx.strokeStyle = colors[index]; // Set the stroke color
  ctx.strokeText('abj'.10 + index * 50, height / 2); // Use stroke to draw text
  ctx.restore();
}
Copy the code

We set all the values of textBaseline again and see the effect shown above. Top, middle, alphabetic, and bottom are common, and the default is alphabetic.

MeasureText is also one of the more popular methods in practice, which measures how much width a given text will occupy if drawn with a currently set text style. Especially when drawing table data or some analysis diagrams, it is necessary to draw illustrative text, but the coordinate of text drawing is determined according to the current mouse position, so as not to exceed the visible area of canvas. This method is simpler to use and returns an object with the width property, which is the measured result. There is no method to measure the height of text on canvas. However, in practice, the width value measured by W letter is often added a little bit, and it can be roughly considered as the height value of the current text.

ctx.font = '18px sans-serif'; // Set font, must be set font property, to measure accurately
let textWidth = ctx.measureText('W').width;
let textHeight = textWidth + textWidth / 6;
console.log('Width of current text W:${textWidth}`);
console.log('Height of current text W:${textHeight}`);
Copy the code

summary

This article is mainly to learn how to use drawImage to draw pictures in Canvas, and how to use getImageData and putImageData to process image pixel values, such as common image gray processing or inverting color. We also reviewed some methods and properties for drawing text in Canvas, which are similar to CSS and easier to understand.