# The Lantern Festival can’t go to the Hangzhou Fireworks Festival. As a front-end, let’s implement a fireworks page by ourselves.

This year Hangzhou has started to hold a fireworks conference again, and the fireworks are really beautiful. If you don’t have the opportunity to watch it live, as a front end, you can implement a fireworks page by yourself.
Come with me and learn this fireworks code to waste.

## Function split

Common fireworks are sent upwards from one place, and when they reach a certain height, they explode into a circle of light spots. The height is different, the size of the circle is different, and the color of the light spot is also different.
Therefore, the process of a firework can be divided into 2 steps: 1. Rising from a certain position P0 to P1; 2. Exploding from P1 into a circle of light spots, the light spots spread outward, and the light spots spread to a certain extent, and then gradually disappeared.

## write code step by step

For this firework, we need to use canvas to draw, so we need to make the canvas first:

```var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// clear canvas
function clearCanvas() {
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
clearCanvas();```

### fireworks lift off

First set the basic information of the fireworks, including the size of the fireworks particles and the position where the fireworks start to be released.

```const size = 2; // The size of the fireworks particles
const start = {
x: canvas.width / 2,
y: canvas.height - 100,
};```

Then make a mouse click event, and when the canvas is clicked, the fireworks will start playing (fire method). In the fire method, the position of the current fireworks particles is constantly updated to form an upward effect of fireworks.

```let rid = 0;
function fire() {
const p = {x: start.x, y: start.y}
function tick() {
clearCanvas();
step1(p);
rid = requestAnimationFrame(tick);
}
cancelAnimationFrame(rid);
tick()
}

// Fireworks first step, take off
function step1(p) {
p.y = p.y - 4; // Change particle position every frame
ctx.beginPath();
ctx.arc(p.x, p.y, size, 0, Math.PI*2, false)
ctx.closePath();
ctx.fillStyle = "#ffffff";
ctx.fill();
}

function mouseDownHandler(e) {
fire();
}

When the firework is rising, the speed will become slower and slower. When it reaches a certain height, the rising speed will reach 0 and enter the second stage. So the change of p.y needs to be changed to change, and then add a control to enter the second state.

```function tick() {
clearCanvas();
// Control phase according to state
if (p.state === 1) {
step1(p);
} else if(p.state === 2) {
step2(p);
}
}
function step1(p) {
if (p.upSpeed < 0.1) { // When the speed is very low, enter the second stage
p.state = 2;
return;
}
p.upSpeed = p.upSpeed - 0.05; // The speed change is gradually reduced
p.y = p.y - p.upSpeed;
...
}```

### fireworks explode

After entering the second stage, you need to explode a bunch of particles that spread around. We first make a circle of particles, and then add animation and particles.

```let radius = 10; // Particle Surrounding Radius
function step2(p) {
const count = 10; // 10 particles
for (var i = 0; i < count; i++) {
var angle = 360/count * i;
let radians = angle * Math.PI / 180;

ctx.beginPath();
ctx.arc(p.x - 5 + vx, p.y - 5 + vy, size, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fillStyle = '#ffffff';
ctx.fill();
}
}```

Then now we want to increase the number of particles, just change the count here, each particle is not evenly distributed, so the angle and radius should be random. There is a step2 that will be re-run every time it is refreshed, so the production of particles needs a separate method, otherwise every time it is refreshed, the particles will be randomly generated again, so the particles will fly randomly.

```// Retrofit method to bring the creation of fireworks together. Spending multiple fireworks at the same time
var fireworks = [];
function createFireworks(p) {
const firework = {
x: start.x,
y: start.y,
upSpeed: 12,
state: 1,
particles: [], // Particles after explosion
};
var count = Math.floor(Math.random() * 100) + 80;
for (var i = 0; i < count; i++) {
var p = {vx:0, vy:0}; // For each particle, vx,vy represent the offset relative to the inflation position
var angle = Math.floor(Math.random() * 360);
p.radians = angle * Math.PI / 180;
p.speed = (Math.random() * 5) + .4;
p.size = Math.floor(Math.random() * 3) + 1;
firework.particles.push(p);
}
}

function step2(f) {
const particles = f.particles;
for (let i = 0, len = particles.length; i < len; i++) {
const p = particles[i];

p.vx += vx;
p.vy += vy;
ctx.beginPath();
ctx.arc(f.x - 5 + p.vx, f.y - 5 + p.vy, size, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fillStyle = '#ffffff';
ctx.fill();
}
}```

At this point, the basic appearance of the fireworks is OK. After a period of time, the fireworks need to disappear, and different styles are used to distinguish the fireworks. It is best to have different angles and starting positions. different.

### Fireworks are different in color and disappear

The disappearance of fireworks is relatively simple, just make a transparency for each particle, the transparency gradually becomes smaller, and finally disappears. For the color, it can be done randomly through hsla.

```function createFireworks() {
const firework = {...};
let count = Math.floor(Math.random() * 100) + 80;
let hue = Math.floor(Math.random() * 6) * 60; // Divided into 6 main colors
for (let i = 0; i < count; i++) {
...
p.hue = Math.floor(Math.random() * 30) + hue; // A slight variation on each main color
p.brightness = Math.floor(Math.random() * 30) + 70;
p.alpha = (Math.floor(Math.random() * 61) + 40) / 100;
...
}
}
function step2(f) {
for (let i = 0, len = particles.length; i < len; i++) {
...
p.alpha -= 0.005; // disappear slowly
...
ctx.fillStyle = 'hsla(' + p.hue + ', 100%, ' + p.brightness + '%, ' + p.alpha + ')'; // hsla color
}
}```

### The starting position and launch angle of fireworks are random

The last thing is to make the firing position and firing angle of the fireworks random.

```function createFireworks() {
const firework = {
x: start.x + Math.floor(Math.random() * 100) - 50, // random position on x
...
};
...
}
function step1(p) {
...
p.x = p.x + p.xAngle; // During the ascent, the change in the x direction
...
}```

Perfect, already comparable to the fireworks effect of the fireworks festival. The last thing is to add some different types of fireworks. I will simply make a heart-shaped one here, and the complicated ones are up to you.

### different types of fireworks

If you want to add a heart-shaped firework, how do you do it? First, you need to split the step2 of the explosion to support multiple types of rendering.

```function step2(f) {
const particles = f.particles;
for (let i = 0, len = particles.length; i < len; i++) {
const p = particles[i];
if (p.alpha <= 0) {
continue;
}
// Depending on the type of fireworks, different rendering methods are used
switch(f.type){
case 1:
step2_circle(p, f.x, f.y); // This is the original round firework
break;
case 2:
step2_heart(p, f.x, f.y);
break;
default:
step2_circle(p, f.x, f.y);
break;
}
}
}

// The fireworks produced by createFireworks add a type attribute to control different types of fireworks
function createFireworks(type) {
const firework = {
...
type: Math.floor(Math.random() * 2) + 1,
};
}```

#### Love type

The last is to make the fireworks of love into a separate rendering method:

```// The effect after the explosion is love
function step2_heart(p, x, y) {
let vx = p.radius * Math.pow(Math.sin(t), 3);
let vy = p.radius / 1.2 * Math.cos(t)
- p.radius / 3.2 * Math.cos(2*t)
- p.radius / 8 * Math.cos(3*t)
- p.radius / 16 * Math.cos(4*t)

p.vx += vx;
p.vy -= vy;
p.alpha -= 0.005;
ctx.beginPath();
ctx.arc(x - 5 + p.vx, y - 5 + p.vy, size, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fillStyle = `hsla(\${p.hue}, 100%, \${p.brightness}%, \${p.alpha})`;
ctx.fill();
}```

### at last

Finally, there is a question, if the firework explosion is a piece of text, how to do it?