Introduction: In the daily development process, we often use canvas to produce some animation effects. Among them, we need to generate a certain number of particles with similar shapes and basically the same behavior. Through the movement of these particles, we can display animation effects, such as rain, twinkling stars… This kind of effect can be called particle system animation. Simply put, a particle system is a collection of particles that emit a stream of particles (an animated effect of the particles) by specifying the emission source (the starting position of each particle).
See specific examples and complete codes in this paper:
Canvas particle animation system solution
Contents of this article:
-
The commonality of particle systems (why build a particle system)
-
Start building a particle system
-
Add off-screen rendering to optimize your particle system
-
Particle system source code use instructions
-
conclusion
1. The commonality of particle systems
First let’s look at a simple particle animation, as shown below:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="example"></canvas>
</body>
<script>
var cvs = document.getElementById('example');
var ctx = cvs.getContext('2d');
var width = 400;
var height = 400;
cvs.width = 400;
cvs.height = 400;
var particle = [];
var lineAnimation;
function createItem(amount) {
for (let i = 0; i < amount; i++) {
particle.push({
posX: Math.round(Math.random() * width),
posY: Math.round(Math.random() * height),
r: 4.color: Math.random() < 0.5 ? '#d63e3e' : '#23409b'
});
}
draw();
};
function draw() {
ctx.clearRect(0.0, width, height);
particle.map((item, index) = > {
ctx.beginPath();
ctx.arc(item.posX, item.posY, item.r, 0.2 * Math.PI);
ctx.fillStyle = item.color;
ctx.fill(); // Draw a solid circle
ctx.closePath();
item.posY = item.posY + 2;
if (item.posY > height) {
item.posX = Math.round(Math.random() * width);
item.posY = Math.round(Math.random() * height);
};
})
lineAnimation = requestAnimationFrame(draw);
}
function stop() {
cancelAnimationFrame(lineAnimation);
}
createItem(100);
</script>
</html>
Copy the code
Analyzing the above code, we can summarize some characteristics of the particle system:
1. Createcanvas
The canvas.
2. Initialize the particle (create the particle shape and determine the starting position of the particle).
3. Draw particles to canvas.
4. Define the motion of particles (i.e. motion animation of particles).
5. Control the play and pause of animation.
6. Clear the canvas.
Since particle systems have so much generality, why can’t we separate out the generality and build a particle system?
Now for the second part of the article,Start building a particle system
Start building a particle system (based on ES6)
Based on the commonalities summarized in the previous section, we can write the general code of a particle system:
const STATUS_RUN = 'run';
const STATUS_STOP = 'stop';
// Particle system base class
class Particle {
//1. Create 'canvas' canvas
constructor(idName, width, height, options) {
this.canvas = document.getElementById(`${idName}`);
this.ctx = this.canvas.getContext('2d'); // Canvas execution context
this.timer = null; // Animation runs the timer, using requestAnimationFrame
this.status = STATUS_STOP; // The animation execution state defaults to stop
this.options = options || {}; // Configuration (particle number, speed, etc.)
this.canvas.width = width;
this.canvas.height = height;
this.width = width;
this.height = height;
this.init();
};
//2. Initialize the particle
init() {
};
//3. Draw particles to canvas
draw() {
let self = this;
let { ctx, width, height } = this;
ctx.clearRect(0.0, width, height);
this.moveFunc(ctx, width, height);
this.timer = requestAnimationFrame((a)= > {
self.draw();
});
};
//4. Define the motion of particles
moveFunc() {
};
//5. Control the playback and pause of animation.
run() {
if (this.status ! == STATUS_RUN) {this.status = STATUS_RUN;
this.draw(); }}; stop() {this.status = STATUS_STOP;
cancelAnimationFrame(this.timer);
};
//6. Clear the canvas
clear() {
this.stop();
this.ctx.clearRect(0.0.this.width, this.height);
};
};
export {
Particle
}
Copy the code
Let’s rewrite the original example with this method:
import { Particle } from ".. /lib/particleI.js";
class exampleMove extends Particle {
//2. Initialize the particle
init() {
this.particle = [];
let amount = this.options.amount;
let { width, height } = this;
for (let i = 0; i < amount; i++) {
this.particle.push({
posX: Math.round(Math.random() * width),
posY: Math.round(Math.random() * height),
r: 4.color: Math.random() < 0.5 ? '#d63e3e' : '#23409b'}); }};//4. Define the motion of particles
moveFunc(ctx, width, height) {
this.particle.map(item= > {
item.posY = item.posY + 2;
if (item.posY > height) {
item.posX = Math.round(Math.random() * width);
item.posY = Math.round(Math.random() * height);
};
this.createParticle(ctx, item.posX, item.posY, item.r, item.color);
});
};
// Particle shape
createParticle(ctx, x, y, r, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, r, 0.2 * Math.PI);
ctx.closePath();
ctx.fill();
};
//4. Define the motion of particles
moveFunc(ctx, width, height) {
this.particle.map(item= > {
item.posY = item.posY + 2;
if (item.posY > height) {
item.posX = Math.round(Math.random() * width);
item.posY = Math.round(Math.random() * height);
};
this.createParticle(ctx, item.posX, item.posY, item.r, item.color);
});
};
}
Copy the code
Create a new instance to make the particle system move:
var example = new exampleMove('example'.400.400, { speed: 3.amount: 8 });
example.run();
Copy the code
At this point, a small particle system is built. Let’s take a look at the summary:
About these commonalities in particle systems:
1. Createcanvas
The canvas. (Base class done)
2. Initialize the particle (create the particle shape and determine the starting position of the particle).
3. Draw particles to canvas (base class done).
4. Define the motion of particles (i.e. motion animation of particles).
5. Control the play and pause of animation (base class complete).
6. Clear the canvas (base class done).
Since the way of displaying particle animation is different for each person, 2 and 4 points need to be inherited and modified by themselves.
Do you think this is the end of the article?
Let’s increase the number of particle systems we just built to 6,000 and take a look:
Frame rate around 30 is very low!! Generally the frame rate should be maintained at60
Otherwise, the animation will appear to be stuck!!
Ps: Regarding the performance analysis, you can see my previous summary: Brother Dei, I heard that your animation is very slow?
So what should we do? Brother?
Now let’s move on to part threeAdd off-screen rendering to optimize your particle system
Add off-screen rendering to optimize your particle system
Before we start, should we analyze why our particle animation freezes after reaching a certain number?
Take a look at the following figure using chrome performance Profiling tool:
It is not hard to see that most of the time consumed in each frame is in the call of Canvas Api.
How to solve this problem?
Var a = ‘#f00’; var a = ‘#f00’; var a = ‘#f00’;
var cvs = document.getElementById('example');
var ctx = cvs.getContext('2d');
var timeStart = (new Date()).getTime();
var count;
for (var i = 0; i < Math.pow(10.7); i++) {
// ctx.fillStyle = '#f00';
count = '#f00';
};
var timeEnd = (new Date()).getTime();
console.log('during:::', timeEnd - timeStart);
Copy the code
So the key to our solution was to minimize the number of calls to render related apis.
This is where our off-screen rendering mechanism comes in!!
The purpose of off-screen rendering is to avoid frequent calls per frameThe number of times the API was rendered
So how to avoid it?
Off-screen rendering principle
We create a separate canvas canvas for each particle and draw the particles on the canvas first.
The following code (complete codeCanvas particle system) :
// Off-screen particle class (try to keep the size of the canvas the same as the particle size, too large canvas will also consume performance);
class offScreenItem {
constructor(width, height, create) {
this.canvas = document.createElement('canvas');
this.width = this.canvas.width = width * 2;
this.height = this.canvas.height = height * 2;
this.ctx = this.canvas.getContext('2d');
// Draw particles on the canvas
create(this.ctx, this.width, this.height);
};
// Move the particle (use the drawImage method to change the position of the particle canvas to move it)
move(ctx, x, y) {
if (this.canvas.width && this.canvas.height) {
ctx.drawImage(this.canvas, x, y); }}}Copy the code
Let’s take a look at the performance of off-screen rendering.
The same is6000
Two particles, but the frame rate has almost come back60
Kaisen!! .
Note:
For example, in the image above, I only have red and blue circles, so I only need to instantiate each particle twice, rather than once. This will consume a lot of memory, and it would be better if I did not enable the off-screen rendering.
About the particle system source code use instructions:
import { Particle, offScreenItem } from ".. /lib/particle.js";
class exampleMove extends Particle {
// Particle shape drawing
createParticle(ctx, x, y, r, color) {
//todo...
};
// How do particles move
moveFunc(ctx, width, height) {
//todo...
};
// Initialization position of off-screen particles
createOffScreenInstance(width, height, amount) {
//todo...
};
// Normal particle initialization position
createNormalInstance(width, height, amount) {
//todo...}}/** * @param {[String]} id [canvas canvas id] * @param {[Number]} width [canvas canvas width] * @param {[Number]} height [canvas canvas height] * @param {[Object]} option [Particle system configuration {speed: 3, amount: 800}] * @param {[Boolean]} offScreen [Whether to use off-screen rendering] * */
var example = new exampleMove(id, width, height, option, offScreen);
/ / sport
example.run();
/ / stop
example.stop();
// Clean up the canvas
example.clear();
Copy the code
Conclusion:
From this article, it should be clear that:
1. What is a particle system?
2. Why do we need to write a particle system?
3. When the number of particles reaches a certain bottleneck, how should we optimize it?
s
canvas
There are many points that can be optimized. Performance problems cannot be solved by one or two general solutions alone. This paper is just one of the directions, hoping to give you some inspiration and thinking.