canvas Animation Reality and Performance Optimization

Interpolate an article on canvas animation and performance optimization to lay the foundation for better access to the world of webgl.

The content of this article may be a little difficult to understand, and I hope you have questions and will ask them in time. Let's stop gossiping and start today's topic.

1. Animated Reality

First, let's introduce what we want to do with the animation: Meteor Source in Night Sky.

Today, let's share with you how to write canvas animations and how to optimize them.

1.1 Build Pages

The page composition of canvas is very simple. As follows:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>canvas Animation and Performance Optimized Reality</title>
  <!-- Introduce page styles -->
  <link rel="stylesheet" href="./style/index.css">
</head>
<body>
  <canvas id="shooting-star">Your browser does not support it canvas Label, please upgrade version or select another browser</canvas>
</body>
</html>
<!-- Introducing Page Functions -->
<script src="./js/index.js"></script>

Create an html file and introduce css and js files.

There's a problem here, and as we shared with you earlier, the width and height of the canvas tag are set in html. But to fit our screen, we have to use js to set the width and height of the canvas.

1.2 Implementation

1. First create a class of meteors. And add a startup method
class ShootingStar{
  // Construction method
  constructor() {}
  // Startup Method
  start() {}
}
// Instantiate an object and start
new ShootingStar().start();
2. Get the canvas object and drawing context, and set the width and height of the canvas

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

// Construction method
constructor() {
	// Get canvas object
  this.ctx = document.getElementById('id Name');
  // Get Drawing Context
  this.c = this.ctx.getContext('2d');
  // Get the width and height of the current page
  this.maxW = document.body.offsetWidth;
  this.maxH = document.body.offsetHeight;
  
  // Define an array to store night sky stars, which will be used in future development
  this.skyList = [];
}
// Initialize the width and height of the canvas
initCanvasSize() {
  this.ctx.width = this.maxW;
  this.ctx.height = this.maxH;
}
// Initialization function called in start method
start() {
  this.initCanvasSize();
}
3. Draw the background

We didn't introduce a picture here. It simulated a starry sky effect, which was a bit crude.

// Draw 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 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 random coordinates into an array
    this.skyList.push({
      x,y,r
    })
  }
}
4. Turn on Shooting Star Mode
// Initialize background and restart shooting
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 Background
  this.drawStarList();
  // Restart Drawing Shooting Stars
  this.startShootingStar();
}
// Draw Shooting Stars
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 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();
}
// Turn on Shooting Star Mode
startShootingStar() {
  // Set the speed of x and y
  let x = ~~(Math.random() * this.maxW);
  let y = 4;
  // Set the color of the shooting star.
  this.c.fillStyle = 'darkorange';
  // Gets the maximum distance a meteor can glide. Disappear after this distance
  const clearY = Math.random() * this.maxH;

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

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

		// Draw a shooting star, passing in the current x, y coordinates
    this.drawStar(x, y);
    // This function is used to tail a meteor.
    this.drawCover();
    // Use this function to animate
    requestAnimationFrame(draw);
  }
  draw()
}

The content of the actual part of the animation is shared here. Interested students can check the source code or try to achieve the following by themselves.

2. Performance optimization

2.1 Use Computing instead of Frequent Rendering

Computing is often used in place of frequent canvas rendering during drawing. It works like a DOM reflux. Because canvas is also part of the dom, too many operations can affect performance. Of course, if you use an algorithm implemented with a particular consumption, that's another matter.

2.2 Using the requestAnimationFrame

In many cases, we're used to using setInterval, setTimeout to animate our pages. There are also many small partners who find this implementation to be frame-dropping. There are two reasons:

  • setInterval, setTimeout depend on the browser's asynchronous loop, so the animation execution time we set may not be the real animation execution time. Maybe the js engine is executing other code at this time, so frame dropping will occur.
  • Refresh frequency affects screen resolution and screen size. Screen refresh rates may vary from device to device.

For these two points, we use the 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 will be executed, and at the time of execution the browser will optimize the invocation of the method.
  • Redraw by frame with the same time interval between page redrawing or reflow as the display refresh interval when the callback function is called through the requestAnimationFrame. So instead of passing the time interval as setTimeout does, the requestAnimationFrame uses the system to get and use the display refresh frequency

2.3 Offscreen Rendering

Offscreen rendering can be understood as creating an alternate canvas but not displaying it on the page, performing a pre-rendering operation. This operation uses drawImage, the first parameter of which can accept either a picture object or a **canvas object**.

Specific implementation:

The content that will need to be manipulated is processed on the off-screen canvas before being placed into the display layer using the drawImage method.

2.4 Layered Canvas

It's also a way to use multiple canvas to render static content separately from content that needs to be computed frequently, and the more complex the scene, the better for this method. The implementation is as follows

Sketch Map:

In this way, our needs can be broken down into modules. Decrease performance consumption by splitting up content that needs to be drawn frequently.

Performance optimization methods are primarily some everyday considerations and splits, not everything.

In the animation implementation of canvas, algorithms also take up a large part, such as particle operations in canvas, which are thousands of pixels at a time. The improper use of algorithms may cause a lot of problems.

Okay, that's all for today's sharing, a temporary break in. Next, we will share the content about webgl, so don't miss it. Bye~

Tags: Javascript Front-end webgl canvas

Posted by Assorro on Mon, 02 May 2022 18:04:35 +0300