This is the fourth in the canvas series of notes to study and review. For the complete notes, see canvas Core Technologies.

In previous articles, we reviewed the basic functions of drawing line segments, graphics, and images on canvas. Powerful animations are necessary when making 2D games or richer graphics libraries. Canvas does not provide animation properties in CSS, but provides basic functions such as Translate, scale, rotate, etc. We can combine these functions to realize animation.

In terms of animation, we also need to understand frame rates. We usually talk about a frame, which is the amount of time that has elapsed since the browser completed drawing once. Modern browsers typically have a frame rate of 60 FPS, which means you can draw 60 times in 1s. If the frame rate is too low, it will feel noticeably stuck. Generally, the higher the frame rate, the smoother the animation. In JavaScript, we’re going to draw 60 times in 1s, which used to be done with setTimeout or setInterval.

setInterval((a)= > {
   // Perform the draw operation
}, 1000 / 60);
Copy the code

This timer ways, although can be achieved, but it is not the best way to it is at a fixed time interval to perform, rendering code is added to the queue browser doesn’t necessarily follow the update frequency synchronization, and heavily dependent on the current execution stack, if one perform performed a lot of complicated calculation in the stack, Then the drawing code we added might not execute at the time interval we set. In H5, modern browsers provide a requestAnimationFrame method to perform the animation update logic. This method will execute the function passed to it on the next update of the browser. We don’t have to worry about the browser frame rate at all and can focus on the animation update logic.

const animate = (a)= > {
  // Perform the draw operation
  requestAnimationFrame(animate);
};
animate();
Copy the code

Of course, to be compatible with previous browsers, we usually need to combine requestAnimationFrame with setTimeout or setInterval to implement polyfill. The simple process is as follows. For better implementation, check raf.js.

function myRequestAnimationFrame(callback) {
  if (requestAnimationFrame) {
    return requestAnimationFrame(callback);
  } else {
    return setTimeout((a)= > {
      if (performance && performance.now) {
        return callback(performance.now());
      } else {
        return callback(Date.now()); }},1000 / 60); }}function cancelMyRequestAnimationFrame(id) {
  if (cancelAnimationFrame) {
    cancelAnimationFrame(id);
  } else{ clearTimeout(id); }}Copy the code

translation

In animation processing, CSS can perform translation operation for a specific element. In canvas, it can only move the coordinate system, thus indirectly changing the position of elements in canvas. In canvas core technology – How to draw a line segment, the knowledge related to Canvas coordinate system is explained in detail. Interested students can go to have a look first. The default origin of the Canvas coordinate system is in the upper left corner, the horizontal right direction is positive X direction, and the vertical downward direction is positive Y direction. By shifting the Canvas coordinate system, you can move the origin of coordinates to a certain area in the Canvas or outside the visible area of the canvas.

// before I shift the coordinate system
ctx.strokeStyle = 'grey'; 
ctx.setLineDash([2.2]);
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke(); 
// Shift coordinate system
ctx.translate(120.20); // Shift the coordinate system 120px to the right and 20px down
ctx.beginPath(); // Start a new path
ctx.strokeStyle='blue'; 
ctx.setLineDash([]); 
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); 
Copy the code

Before translating, we draw a rectangle with 100 sides at coordinates (10, 10), as shown in the gray dotted rectangle. Then we call CTx.Translate (120,20) and translate the coordinate system 120 pixels to the left and 20 pixels to the bottom. After that, we have the same rectangle at coordinates (10, 10). 10) draws a rectangle with side length of 100, as shown in the blue solid line rectangle. For these two rectangles, the coordinates and the sides that we drew haven’t changed, but the coordinate system has been shifted, so the positions that we drew have also changed.

The coordinate system translation is shown as follows,

The zoom

Coordinates can be scaled as well as translated. Canvas provides ctx.scale(x,y) to scale the x and y axes. By default, the canvas scale factor is 1.0, which means that in the Canvas coordinate system, 1 unit means the length of the drawing 1px. If the scale factor is changed to 0.5 through the scale function, then in the Canvas coordinate system, 1 unit means the length of the drawing 0.5px. The original graph was drawn at half the size.

ctx.strokeStyle = 'grey'; 
ctx.fillStyle = 'yellow'; 
ctx.globalAlpha = 0.5; 
ctx.fillRect(0.0, width, height); // Fill the entire canvas area
ctx.globalAlpha = 1;
ctx.setLineDash([2.2]); 
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke(); 
ctx.scale(0.5.0.5); // Scale the coordinate system to 0.5 for both X and Y axes
ctx.beginPath(); // Start a new path
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height); // Fill the entire canvas area after scaling
ctx.globalAlpha = 1;
ctx.setLineDash([]); 
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); 
Copy the code

As you can see, we have shrunk both the X and Y axes to half their original size. The newly drawn rectangle (solid red line) is not only half its original width and height, but also has changed the position of the upper-left coordinate. What we need to understand here is that we are scaling for the coordinate system. The yellow area is the Canvas coordinate system area before scaling, and the green area is the Canvas coordinate system area after scaling 0.5.

ctx.scale(0.5.1); // Scale the coordinate system to 0.5 on the X axis and the same on the Y axis
Copy the code

You can set the scaling factor of X axis and Y axis to be different. For example, the scaling factor of X axis can be reduced to 0.5, while the Y axis remains unchanged. After scaling, the Canvas area on the X axis will become half of the original one.

There are some other tricks, such as making a mirror image, and scaling ctx.scale(-1,1) will draw a symmetric mirror image of the Y-axis. Similarly, scale ctx.scale(1,-1) to draw a symmetric mirror image of the X-axis.

ctx.font = '18px sans-serif';
ctx.textAlign = 'center';
ctx.translate(width / 2.0); // First shift the coordinate system to the middle of the X axis
ctx.strokeStyle = 'grey'; // Set stroke style
ctx.fillStyle = 'yellow';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([2.2]); // Set the dotted line
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke(); / / stroke
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
ctx.scale(- 1.1); // Scale the coordinate system to 0.5 for both X and Y axes
ctx.beginPath(); // Start a new path
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red'; // Set stroke style
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); / / stroke
Copy the code

As shown in the figure, we have achieved a symmetric mirror image on the Y axis. Before scaling, we have shifted the coordinate system to the middle of the X axis, because otherwise, after scaling, the drawn part will be outside the visible area of the canvas and will not be visible.

rotating

It can pass in canvasctx.rotate(angle)To implement the rotation of the coordinate system, parametersangleIt’s radians, not degrees. One Angle is equal to, the Angle of radian should be carried out before calling, and the calculation formula is as follows:

// Convert the Angle to radians
function toAngle(degree) {
  return (degree * Math.PI) / 180;
}
Copy the code

Let’s look at an example of rotating the coordinate system by 15 angles, as follows,

ctx.font = '18px sans-serif';
ctx.textAlign = 'center';
ctx.strokeStyle = 'grey'; // Set stroke style
ctx.fillStyle = 'yellow';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([2.2]); // Set the dotted line
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke(); / / stroke
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
ctx.rotate(15* Math.PI/180); // Rotate the coordinate system 15 angles
ctx.beginPath(); // Start a new path
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red'; // Set stroke style
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); / / stroke
Copy the code

The yellow area is the original default Canvas coordinate system area after rotation, and the green area is the coordinate system area after rotation. It can be seen that the rotation operation actually rotates the entire Canvas coordinate, and the contents in the canvas will be rotated accordingly. The argument Angle passed in can be either positive or negative, with a positive indicating clockwise rotation and a negative indicating counterclockwise rotation.

ctx.rotate(- 15* Math.PI/180);  // Rotate counterclockwise by 15 degrees
Copy the code

These are easy to use and easy to understand. In practice, you can translate, scale and rotate the Canvas coordinate system at the same time. In this case, we can separately use the above methods for corresponding operations, their effects are superimposed. In the Canvas, there’s actually a method that allows you to pan, scale, and rotate at the same time. Let’s take a look at the magic of this method.

transform

In the coordinate system data transformation, the most common means is to model the identity matrix, and then transform the identity matrix. In fact, all of the translation, scaling, and rotation that I mentioned above are applied to the matrix. The canvas ctx.transform(a, B, C, D,e,f) provides 6 parameters. The matrix is stored longitudinally in canvas and represents the matrix,


In 2-dimensional coordinates, representing a point (x, y), in order to do the matrix transformation, we need to extend the standard 2-dimensional coordinates to 3-dimensional coordinates, and we need to add one more dimensionwThis is the 2-dimensional homogeneous coordinate system (x, y, w). A homogeneous point (x, y, w) mapped to the actual 2-dimensional coordinate system is (x/w, y/w). If we want to point x, y, w to the actual 2-dimensional coordinate system is x, y, so we just set itThat’s it. More on thatHomogeneous coordinates. And then by multiplying the matrices, I get the following formula,


So let’s look at shifting, let’s look at shifting one point (x, y) to another point (x prime, y prime). The formula is as follows,


So if you substitute the translation formula into the matrix transformation formula that we’ve derived up here,...... We usetransformTo implement panning, you just callCTX. Transform (1,0,0,1, dx, dy), effect and callctx.translate(dx,dy)The same.

// before I shift the coordinate system
ctx.strokeStyle = 'grey';
ctx.setLineDash([2.2]);
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke();
// Shift coordinate system
/ / CTX. Translate (120); // Shift the coordinate system 120px to the right and 20px down
ctx.transform(1.0.0.1.120.20); // Use transform to translate
ctx.beginPath(); // Start a new path
ctx.strokeStyle = 'blue';
ctx.setLineDash([]);
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke();
Copy the code

Ctx.translate (120,20); Transform (1, 0, 0, 1, 120, 20); You get the same effect.

Let’s look at scaling again, so when we scale a point (x, y) through frame K, we get a new point (x’, y’). The formula is as follows,


We also substitute the scaling formula into the matrix transformation formula, and we get...... We usetransformTo scale, just callCTX. Transform (k, 0, 0, k, 0, 0), effect and callctx.scale(k,k)The same.

ctx.strokeStyle = 'grey';
ctx.fillStyle = 'yellow';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height); // Fill the entire canvas area
ctx.globalAlpha = 1;
ctx.setLineDash([2.2]);
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke();
/ / CTX. Scale (0.5, 0.5); // Scale the coordinate system to 0.5 for both X and Y axes
ctx.transform(0.5.0.0.0.5.0.0); // Use transform to scale
ctx.beginPath(); // Start a new path
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height); // Fill the entire canvas area after scaling
ctx.globalAlpha = 1;
ctx.setLineDash([]);
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); 
Copy the code

Transform (0.5,0,0,0.5,0,0) has the same effect as ctx.scale(0.5,0.5).

And finally, rotation, let’s put a coordinate (x, y) at the rotation AngleAnd then I get my new coordinates (x prime, y prime), and the formula is,


The above formula is calculated according to the formula of the sum difference between two angles of a triangle. See derivation for details2D Rotation. Similarly, if we substitute the rotation formula into the matrix transformation formula, we get...... We callwithIt’s the same. Notice what we have hereIt’s radians.

ctx.font = '18px sans-serif';
ctx.textAlign = 'center';
ctx.strokeStyle = 'grey'; // Set stroke style
ctx.fillStyle = 'yellow';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([2.2]); // Set the dotted line
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke(); / / stroke
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
// ctx.rotate(15* Math.PI/180); // Rotate the coordinate system 15 angles
let angle = (15 * Math.PI) / 180; // Calculate the radian value
let cosAngle = Math.cos(angle); // Compute the cosine
let sinAngle = Math.sin(angle); // Calculate sine
ctx.transform(cosAngle, sinAngle, -sinAngle, cosAngle, 0.0); // Use transform rotation
ctx.beginPath(); // Start a new path
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red'; // Set stroke style
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); / / stroke
Copy the code

Transform (cosAngle,sinAngle, -sinangle,cosAngle,0,0) has the same effect as ctx.rotate(Angle).

The above three basic coordinate system operation methods can be realized by transform. Through combination, we can set the translation, rotation and scaling of the coordinate system at one time, and only need to calculate the correct A, B, C, D, E and F. For example, translate+scale+rotate and transform are implemented at the same time.

  • translate+scale+rotateCombination to achieve
ctx.font = '18px sans-serif';
ctx.textAlign = 'center';
ctx.strokeStyle = 'grey'; // Set stroke style
ctx.fillStyle = 'yellow';
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([2.2]); // Set the dotted line
ctx.rect(10.10.100.100); // Draw a rectangle
ctx.stroke(); / / stroke
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
let angle = (15 * Math.PI) / 180;
ctx.translate(120.20); / / translation first
ctx.scale(0.5.0.5); / / to zoom
ctx.rotate(angle);// Finally rotate
ctx.beginPath(); // Start a new path
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red'; // Set stroke style
ctx.globalAlpha = 0.5;
ctx.fillRect(0.0, width, height);
ctx.globalAlpha = 1;
ctx.setLineDash([]); // Set a solid line
ctx.strokeText('I am the word'.60.60);
ctx.rect(10.10.100.100); // Draw the same rectangle
ctx.stroke(); / / stroke
Copy the code

  • transformOne-time implementation
let angle = (15 * Math.PI) / 180;
// ctx.translate(120, 20);
/ / CTX. Scale (0.5, 0.5);
// ctx.rotate(angle);
let cosAngle = Math.cos(angle);
let sinAngle = Math.sin(angle);
ctx.transform(0.5 * cosAngle, 0.5 * sinAngle, 0.5 * sinAngle, 0.5 * cosAngle, 120.20);
Copy the code

Translate +scale+rotate rotate is the same as transform.


Three matrices are multiplied, respectively, translation matrix * scaling matrix * rotation matrix, according to the calculated matrix, finally substituted into the formula, can be obtained......

If the transform is called multiple times, the effect is also superimposed. For example, we can also use transform separately to achieve the above translation, scaling, rotation,

ctx.transform(1.0.0.1.120.20); // Use transform to translate
ctx.transform(0.5.0.0.0.5.0.0); // Use transform to scale
ctx.transform(cosAngle, sinAngle, -sinAngle, cosAngle, 0.0); // Use transform rotation
Copy the code

A second transform call to scale is made in the coordinate system after the first translation, and a third transform call to rotate is made on the first and second results. Canvas provides the setTransform function, which is similar to the transform function and also accepts a, B, C, D, E and f6 parameters, and the parameter meaning is the same as that in transform. The difference from Transform is that it does not superimpose the effect of matrix transformation. It resets the current coordinate matrix to the default element matrix before performing the same matrix transformation as the Transform. So, if we don’t want to call stack multiple times when calling the Transform matrix, we can use setTransform instead. There is also an experimental function called resetTransform, which resets the current coordinate matrix to the default element matrix, removing the transformation effect applied to the default coordinate system. Note that this is an experimental function and is not supported by many browsers, so it is not recommended. Through analysis, we can get,


summary

This article mainly studies and reviews the transformation of canvas coordinate system. We realize the transformation of Canvas coordinate system through matrix transformation, including translate, scale, rotate, Transform and setTransform. Powerful animation effects can be achieved through combination. In fact, animation effects should continue to change over a period of time. In this article, only a single change has been studied, and animation factors such as time have not been covered. In the next article, we will study and review the advanced knowledge of animation, including time factor, physics factor, time warp change function, etc.