# Teach you to realize the "burst" of wechat 8.0 🎉 Expression effects

## Write at the beginning

Recently, wechat has updated 8.0. One of the most fun things is the update of expression package. Everyone has played the expression package war in the group. As a front-end programmer, this aroused my curiosity. Although I have never implemented such animation, I still can't help but want to implement it. Finally, I spent two days looking at the source code of some libraries to achieve a similar effect. Here I summarize and teach you how to learn and implement it. and 🎉 There is a name of its own, called colorful paper scraps, and its English name is "confetti".

Chat room + colorful confetti effect online address: https://www.qiufengh.com/#/  For the reason of time, I only realized the color block of parallelogram, and the principle of other shapes is similar. You can also set the direction ## Preliminary study

Before writing this special effect, I hardly knew how to use canvas, although I don't know how to use it now, and many API s are not clear. Therefore, this tutorial is also written based on zero basis canvas. You don't have to worry that this tutorial is too difficult to be discouraged. I will realize it step by step on the basis of zero basis canvas. However, before learning this special effect, you need a little knowledge of high school mathematics. If you still remember sin and cos functions, the following contents will be very simple for you. It doesn't matter~

Personally, I prefer to explore and research, and I will study interesting things. Therefore, on the basis of giants, I went to codepen to check many similar implementations for research. Finally, the target is canvas confitti. Why is this library? Because its effect is very good for us, and it is an open source library, and has a {1.3K} star (I feel I can analyze the principle of the boss's implementation library another day ~), and the maintenance frequency is also very high.

## Core implementation

#### Slice scene

I was a little happy when I got this library first, because this library has only one single file. But when I opened this file, I found that it was wrong One file has 500 lines of code. I peel off layers of custom configured code, and finally extract the motion track of a single piece of paper. I began to constantly observe its trajectory Infinite loop observation You can see that it is doing a motion similar to a parabola, and then I label the variables in the source code one by one, and then combine it with the source code. ```fetti.x += Math.cos(fetti.angle2D) * fetti.velocity;
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;
```

It's ok if I don't understand the above code. I just prove the writing method in the source code and provide some ideas for learning the source code. The following is the real opening implementation!

#### Implementation of parallelogram

Before implementing this feature, we need to know several functions of canvas. More view( https://www.runoob.com/jsref/dom-obj-canvas.html )

beginPath

Method to start a path or reset the current path.

moveTo

Move the path to the specified point in the canvas without creating a line.

lineTo

Add a new point, and then create a line from that point to the last specified point in the canvas.

closePath

Creates a path from the current point back to the starting point.

fill

Fills the current drawing (path).

fillStyle

Sets or returns the color, gradient, or mode used to fill a painting.

Since we want to realize colorful paper scraps, I must realize a paper scraps first. Let's realize a parallelogram paper scraps!

We all know that the implementation of parallelogram in css is a div, which is a box by default, but it is not so convenient in canvas, so how to implement a parallelogram?

Four points, we only need to know four points to determine a parallelogram. The coordinate system in canvas is slightly different from our ordinary web pages. It takes the upper left corner as the starting point, but it does not affect. I can draw a parallelogram with a width of 20, (0, 0), (0, 20), (20,20), (20,0).

```...(Some pre initialization code is omitted)
var context = canvas.getContext('2d');
// Clear canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// Set the color and start drawing
context.fillStyle = 'rgba(2, 255, 255, 1)';
context.beginPath();
// Set several points
var point1 = { x: 0, y: 0 }
var point2 = { x: 0, y: 20 }
var point3 = { x: 20, y: 20 }
var point4 = { x: 20, y: 0 }
// Draw 4 points
context.moveTo(Math.floor(point1.x), Math.floor(point1.y));
context.lineTo(Math.floor(point2.x), Math.floor(point2.y));
context.lineTo(Math.floor(point3.x), Math.floor(point3.y));
context.lineTo(Math.floor(point4.x), Math.floor(point4.y));
// Complete the route and fill in
context.closePath();
context.fill();
``` To sum up, we only need one point to determine the initial position of the parallelogram (0, 0). If we know another angle (90 degrees) and the variable length of the parallelogram (20), we can determine the position of the whole parallelogram! (only junior high school knowledge is needed to locate the whole parallelogram).

Well, you've taken a big step towards success by learning to draw this! Isn't it simple~

Bosses: that's it?

Well, that's it.

#### path of particle

By constantly debugging the track motion of each frame of canvas confitti, it is found that it always does an x-axis variable deceleration motion (it will not continue to move until the speed is 0), and the y-axis is also a variable deceleration motion first and then an average speed motion. The following is the approximate track diagram. This is his trajectory. Don't think it looks very difficult, but the core code is only three sentences.

```// fetti.angle2D is an angle (this angle determines a value between trajectory 3 / 2 * Math.PI - 2 * Math.PI. The above range is selected because the trajectory must move in the negative direction to move to the upper left corner),
// fetti.velocity is a value with an initial length of 50.
// fetti.gravity = 3
fetti.x += Math.cos(fetti.angle2D) * fetti.velocity; // fetti.x coordinate of the first point
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // fetti.y coordinate of the first point
fetti.velocity *= 0.8；
```

To sum up, the x cycle of the first coordinate point is always increasing by a negative value (Math.cos(3 / 2 * Math.PI - 2 * Math.PI) is always negative), and this value is decreasing. The y-axis of the first point is always added with a negative value math Cos (3 / 2 * math.pi - 2 * math. PI) is always negative), but due to {fetti Gravity is always positive, so at a critical point, the value of y will continue to increase.

I simulated the following coordinates. In order to let you understand this trajectory, the following coordinate axes are opposite to those in canvas. I also processed the data in the opposite direction. Use a square with 10 sides to realize the trajectory.

```const fetti = {
"x": 445,
"y": 541,
"angle2D": 3 / 2 * Math.PI + 1 / 6 * Math.PI,
"color": {r: 20, g: 30, b: 50},
"tick": 0,
"totalTicks": 200,
"decay": 0.9,
"gravity": 3,
"velocity": 50
}
var animationFrame = null;
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'rgba(2, 255, 255, 1)';
context.beginPath();
fetti.x += Math.cos(fetti.angle2D) * fetti.velocity; // First point
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // First point

var x1 = fetti.x;
var y1 = fetti.y;

var x2 = fetti.x;// Second point
var y2 = fetti.y + 10; // Second point

var x3 = x1 + 10;
var y3 = y1 + 10;

var x4 = fetti.x + 10;
var y4 = fetti.y;

fetti.velocity *= fetti.decay;

context.moveTo(Math.floor(x1), Math.floor(y1));
context.lineTo(Math.floor(x2), Math.floor(y2));
context.lineTo(Math.floor(x3), Math.floor(y3));
context.lineTo(Math.floor(x4), Math.floor(y4));

context.closePath();
context.fill();
animationFrame = raf.frame(update);
}
```

Does it smell like that except for color and shape? #### Reverse effect

So how to make this fall more natural and have a feeling of falling?

In fact, he has been doing a flip effect To disassemble them is to rotate around a point. The whole process is to turn itself over and move according to the motion track. To realize this special effect, in fact, it was mentioned before when realizing a square to realize a square. Satisfying the following three points can realize a parallelogram.

• Know the location of a point

• Know an angle

• Know the length of one side

At present, what I can determine is that the position of a point is easy to determine, which is our starting point. Then we know the side length, which is one angle away. As long as our angle changes constantly, we can achieve the above special effects.

```const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'rgba(2, 255, 255, 1)';
context.beginPath();

fetti.velocity *= fetti.decay;
fetti.tiltAngle += 0.1 // Keep changing the angle of this quadrilateral

var length = 10;

var x1 = fetti.x;
var y1 = fetti.y;

var x2 = fetti.x + (length * Math.sin(fetti.tiltAngle));// Second point
var y2 = fetti.y + (length * Math.cos(fetti.tiltAngle)); // Second point

var x3 = x2 + 10;
var y3 = y2;

var x4 = fetti.x + length;
var y4 = fetti.y;

context.moveTo(Math.floor(x1), Math.floor(y1));
context.lineTo(Math.floor(x2), Math.floor(y2));
context.lineTo(Math.floor(x3), Math.floor(y3));
context.lineTo(Math.floor(x4), Math.floor(y4));

context.closePath();
context.fill();
animationFrame = raf.frame(update);
}

```

In this way, we have realized the above special effects.

#### Combined motion

Then combine what we have written above to form a complete special effect.

```const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'rgba(2, 255, 255, 1)';
context.beginPath();
fetti.x += Math.cos(fetti.angle2D) * fetti.velocity; // First point
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // First point

fetti.velocity *= fetti.decay;
fetti.tiltAngle += 0.1 // Keep changing the angle of this quadrilateral

var length = 10;

var x1 = fetti.x;
var y1 = fetti.y;

var x2 = fetti.x + (length * Math.sin(fetti.tiltAngle));// Second point
var y2 = fetti.y + (length * Math.cos(fetti.tiltAngle)); // Second point

var x3 = x2 + 10;
var y3 = y2;

var x4 = fetti.x + length;
var y4 = fetti.y;

context.moveTo(Math.floor(x1), Math.floor(y1));
context.lineTo(Math.floor(x2), Math.floor(y2));
context.lineTo(Math.floor(x3), Math.floor(y3));
context.lineTo(Math.floor(x4), Math.floor(y4));

context.closePath();
context.fill();
animationFrame = raf.frame(update);
}
```

#### Final form

If you want to achieve the final state, there are many small blocks, fading and random colors!

Set how many frames disappear. Here we have two variables totalTicks and tick to customize to control how many frames the small blocks disappear.

As for multiple small blocks, we only need to make a for loop.

And random colors, made a list of colors.

```const colors = [
'#26ccff',
'#a25afd',
'#ff5e7e',
'#88ff5a',
'#fcff42',
'#ffa62d',
'#ff36ff'
];
var arr = []
for (let i = 0; i < 20; i++) {
arr.push({
"x": 445,
"y": 541,
"velocity": (45 * 0.5) + (Math.random() * 20),
"angle2D": 3 / 2 * Math.PI + Math.random() * 1 / 4 * Math.PI,
"tiltAngle":  Math.random() * Math.PI,
"color": hexToRgb(colors[Math.floor(Math.random() * 7)]),
"shape": "square",
"tick": 0,
"totalTicks": 200,
"decay": 0.9,
"random": 0,
"tiltSin": 0,
"tiltCos": 0,
"gravity": 3,
})
}
```

See the complete code

https://github.com/hua1995116/node-demo/blob/master/confetti/%E5%AE%8C%E6%95%B4demo.html

To realize the expression war of the war form of multi-human. In our wechat, the expression sending is not a single point, but a multi person form. Therefore, we can continue to explore and use the combination of websocket and colorful blocks.

Here we need to pay attention to several points. (due to space reasons, I won't explain websocket. Let's mention the key points of implementation.).

• We can use a tag to distinguish between historical messages and real-time messages
• Distinguish whether it is sent by oneself or receive other people's message to change the direction of confetti.
• Only for single 🎉 Animation will only be carried out when.
• First zoom in and out animation, delay 200ms, and then come out with special effects
```if(this.msg === '🎉' && this.status) {
this.confetti = true;
const rect = this.\$refs.msg.querySelector('.msg-text').getBoundingClientRect();
if(rect.left && rect.top) {
setTimeout(() => {
confetti({
particleCount: r(100, 150),
angle: this.isSelf ? 120 : 60,
origin: {
x: rect.left / window.innerWidth,
y: rect.top / window.innerHeight
}
});
}, 200)
}
}
``` ## More exploration

When drawing very many squares with canvas, we will compare carton. At this time, we can use web worker to calculate, so as to improve performance. Please explore it by yourself, or see the source code of canvas confitti~

## last

Looking back at the author's previous highly praised articles, you may gain more!

• Talking about front-end watermark from cracking a design website (detailed tutorial): 790 + praise

• The front-end novice guide I learned from the glory of the king: 260 + likes

• One article takes you to unlock the mystery of "file download": 140 + likes

• 10 cross domain solutions (with the ultimate trick): 940 + likes

• One article to understand the whole process of file upload (1.8w word in-depth analysis, advanced necessary): 260 + likes

## epilogue

❤️ Follow + like + collect + comment + forward ❤️， Original is not easy, encourage the author to create better articles

Note the official account of autumn wind, a front end official account focused on front-end interview, engineering and open source. • Reply your resume after paying attention and get 100 + sets of exquisite resume templates
• After paying attention, reply to your friends and pull you into the technical exchange group + interview exchange group
• Welcome to the notes of autumn wind

Posted by Dude0 on Tue, 19 Apr 2022 03:31:33 +0930