We interrupt an article on Canvas animation and performance optimization to lay a foundation for us to better enter the world of WebGL.

The content of this article may be a little difficult to understand, I hope you have any questions. Let’s get down to business without further ado.

1. Actual animation

First introduce the animation content we want to achieve: the source of the meteor in the night sky.

Today I will share with you how to write canvas animation and how to optimize.

1.1 Setup Page

Canvas page composition is very simple. As follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Canvas animation and performance optimization</title>
  <! -- Introduce page styles -->
  <link rel="stylesheet" href="./style/index.css">
</head>
<body>
  <canvas id="shooting-star">Your browser does not support the Canvas tag, please upgrade or choose another browser</canvas>
</body>
</html>
<! -- Introducing page functionality -->
<script src="./js/index.js"></script>
Copy the code

Create an HTML file and import CSS and JS files.

There is a problem here, as we shared earlier, that the width and height of the canvas tag must be set in HTML. But to fit our screen, we use JS to set the width and height of the canvas.

1.2 Concrete Implementation

1. First create a meteor class. And add a startup method
class ShootingStar{
  // constructor
  constructor() {}
  // Start method
  start(){}}// Instantiate an object and start it
new ShootingStar().start();
Copy the code
2. Get tocanvasObject and drawing context, and setcanvasThe width and height of

Note: The width and height are set here because the canvas will appear blurred.

// constructor
constructor() {
	// Get the canvas object
  this.ctx = document.getElementById('id name');
  // Get the drawing context
  this.c = this.ctx.getContext('2d');
  // Get the width of the current page
  this.maxW = document.body.offsetWidth;
  this.maxH = document.body.offsetHeight;
  
  // Define an array of stars in the night sky, which will be used later in the development
  this.skyList = [];
}
// Initialize the width and height of the canvas
initCanvasSize() {
  this.ctx.width = this.maxW;
  this.ctx.height = this.maxH;
}
// Call the initialization function in the start method
start() {
  this.initCanvasSize();
}
Copy the code
3. Paint the background

Here we did not introduce the picture, is to simulate the effect of a starry sky, slightly simple.

// Draw the background
drawBackground() {
  // Number of stars in the night sky
  const maxCount = 500;
  // Create a solid black background
  this.c.fillRect(0.0.this.ctx.width, this.ctx.height);

  // Create random coordinates. Random within the current page
  for (let i = 0; i < maxCount; i++) {
    const r = ~~(Math.random() * 3);
    const x = ~~(Math.random() * this.maxW);
    const y = ~~(Math.random() * this.maxH);

    // Push the random coordinates into the array
    this.skyList.push({
      x,y,r
    })
  }
}
Copy the code
4. Enable meteor mode
// Initialize the background and start drawing the meteor again
initBackground() {
  this.c.beginPath();
  this.c.fillStyle = 'rgba (0,0,0,0.2)';
  // Empty the current canvas
  this.c.rect(0.0.this.maxW, this.maxH);
  this.c.fill();
  // Redraw the background
  this.drawStarList();
  // Start drawing the meteor again
  this.startShootingStar();
}
// Draw a meteor
drawStar(x, y) {
  this.drawStarList()
  this.c.beginPath();
  this.c.fillStyle = 'white';
  this.c.arc(x,y,2.0.Math.PI * 2);
  this.c.fill();
}
// Add a trailing effect
drawCover() {
  this.c.beginPath();
  this.c.fillStyle = 'rgba (0,0,0,0.06)';
  this.c.rect(0.0.this.ctx.width,this.ctx.height);
  this.c.fill();
}
// Enable meteor mode
startShootingStar() {
  // Set the speed of x and y
  let x = ~~(Math.random() * this.maxW);
  let y = 4;
  // Set the color of the meteor.
  this.c.fillStyle = 'darkorange';
  // Get the maximum distance a meteor can fall. It disappears at this distance
  const clearY = Math.random() * this.maxH;

  // Draw the function.
  const draw = () = > {
    x -= 1;
    y += 4;

    // If the current slide distance is greater than the maximum distance, initialize the current background.
    if (y >= clearY) {
      this.initBackground();
      return;
    }

		// Draw a meteor, passing in the current x and y coordinates
    this.drawStar(x, y);
    // This function is used to make the meteor have a trailing effect.
    this.drawCover();
    // Use this function to animate
    requestAnimationFrame(draw);
  }
  draw()
}
Copy the code

Here animation combat part of the content is shared, interested students can view the source code, you can also try to achieve the following.

2. Performance optimization

2.1 Use calculations instead of frequent rendering

Instead of frequent canvas rendering, calculations are often used during rendering. The principle is similar to DOM backflow. Because canvas is also part of DOM, too many operations will affect performance. Of course, if you use an algorithm that is particularly expensive to implement, it’s a different story.

2.2 use requestAnimationFrame

In many cases, we are used to using setInterval and setTimeout to animate pages. There are also many friends who find this implementation will lose frames. There are two reasons:

  • SetInterval and setTimeoutDepending on the browser’s asynchronous loop, the animation execution time we set may not be the actual animation execution time, perhaps at this timeJs engineOther code is being executed, so you lose frames.
  • Refresh frequency depends on screen resolution and screen size. The screen refresh rate may vary from device to device.

For both of these, we used requestAnimationFrame to optimize the animation implementation. It has two distinct advantages over the former

  • It is up to the system to decide when the callback is executed, and the browser optimizes the method invocation during execution.
  • Redraw by frame, passrequestAnimationFrameThe interval between page redrawing or rewinding caused by callbacks is the same as the interval between display refresh. sorequestAnimationFrameDon’t need to likesetTimeoutInstead, the browser picks up and uses the display refresh frequency from the system

2.3 Off-screen rendering

Off-screen rendering can be understood as creating an alternate canvas but not displaying it on the page, performing pre-rendering operations. This operation uses the drawImage method, whose first argument can take either a picture object or a Canvas object.

Concrete implementation:

Process the content to be manipulated on the off-screen canvas first, and then use the drawImage method to put it into the display layer.

2.4 Layered Canvas

It is also a way of using multiple canvas to separate render static content and content requiring frequent calculation. The more complex the scene, the more suitable to use this method. The concrete implementation is as follows

Schematic diagram:

This way we can break down our requirements into modules. Break out the content that needs to be drawn frequently to reduce the performance cost.

The performance optimization approach is mainly a number of daily attention points and split methods, not a panacea.

In the animation implementation of canvas, algorithm also occupies a large part. For example, particle operation in Canvas often involves tens of thousands of pixels. Improper use of algorithm may cause great problems.

Well, that’s all for today’s sharing, an impromptu piece of content. Next we will share webGL content, if you are interested in it, don’t miss it. Bye~