Written in the beginning
Recently, wechat updated 8.0, one of the most fun is the update of the emoji, everyone in the group have played the emoji war.
As a front-end programmers, it reminded me of my curiosity, although I’ve never been to realize this animation, but I still can’t help but want to achieve, in the end I took 2 days to see some library source code to achieve a similar effect on my own, I summarize here, and hand and teach you how to learn. And 🎉 has a name of its own. It’s called confetti.
Chat room + Confetti online address: www.qiufengh.com/#/
Chat room Github: github.com/hua1995116/…
Confetti Github: github.com/hua1995116/…
Special effects preview, due to time I only implemented the parallelogram color pieces, other shapes are similar principle.
You can also set the direction
The early stage of the study
I barely knew how to use Canvas before I wrote this effect, although I still don’t know much about it now, and I don’t know much about the API, so this tutorial is based on a zero-base canvas, so you don’t have to worry about being told off because it’s too difficult. I will implement it step by step on the basis of zero base Canvas. You’ll need a little high school math before you can learn this trick. If you remember sines and cosines, the following should be easy for you
I personally like to explore research, and I will do research on interesting things, so I also stood on the basis of giants, and went to Codepen to look up many similar implementations for research.
Finally, the target was Canvas-Confetti. Why this library? Because its effect is very good for us, and it is an open source library, and has 1.3K star (I feel I can analyze the principle of the library later), maintenance frequency is very high.
The core to realize
Slice the scene
When I first got the library, I was kind of happy because it was a single file.
However, when I opened the file, it was wrong… A file of 500 lines of code, I stripped layers of some custom configured code, and finally extracted the movement of a single scrap of paper. I’ve been watching it continuously… An infinite loop of observation…
You can see it’s doing a parabolic motion, and then I annotate the variables in the source code one by one, and then combine the source code.
fetti.x += Math.cos(fetti.angle2D) * fetti.velocity;
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;
Copy the code
The above code can not understand also fine, I just prove the source code in writing, and provide some ideas to learn source code, the following is the real start to achieve!
Implementation of the parallelogram
To implement this feature, we need to know several canvas functions. For more information (www.runoob.com/jsref/dom-o…
beginPath
Method to start a path, or reset the current path.
moveTo
Moves the path to the specified point on the canvas without creating a line.
lineTo
Add a new point and then create a line on the canvas from that point to the last specified point.
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 the painting.
Since we are going to implement confetti, I must implement a confetti first, so let’s implement a parallelogram confetti!
We all know that in CSS, a parallelogram is a div, and the default is a box. In Canvas, it is not that convenient, so how to implement a parallelogram?
Four points, we only need to know four points to identify a parallelogram. The coordinate system in canvas is slightly different from our ordinary page writing. It starts from the top left corner, but it doesn’t matter.
I can draw a parallelogram of width 20, (0, 0), (0, 20), (20,20), (20,0).
. (Omitted some preinitialization code)var context = canvas.getContext('2d');
// Clear the 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 four 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 it
context.closePath();
context.fill();
Copy the code
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 length of the parallelogram (20), we can determine the position of the entire parallelogram! (It only takes junior high school knowledge to locate the entire parallelogram.)
Well, you’ve taken a big step towards learning to draw this! Isn’t that simple
Bosses inside OS: Is that it?
Yeah, that’s it.
trajectory
By continuously debugging the trajectory of Canvas-Confetti for each frame, it is found that it is always doing a variable deceleration on the X-axis (until the speed reaches zero), and a variable deceleration and then an average speed on the Y-axis. The following is a general trajectory diagram.
This is his trajectory, and don’t think it’s hard, 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, which is a negative direction for the trajectory 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 The x coordinate of the first point
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // fetti. Y The y coordinate of the first point
fetti.velocity *= 0.8;Copy the code
In summary, the x-week value of the first coordinate is always increasing by a negative value (Math.cos(3/2 * Math.pi-2 * Math.pi) is always negative), and the value is decreasing. Cos (3/2 * math.pi – 2 * Math.pi) is always negative), but because fetti. Gravity is always positive, at some point the value of y increases.
I simulated the following coordinates, because in order for you to understand the trajectory, the following axes are opposite to the canvas, and I also processed the data in the opposite direction.
Use a square with a 10 on the side to implement 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; // The first point
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // The first point
var x1 = fetti.x;
var y1 = fetti.y;
var x2 = fetti.x;// The second point
var y2 = fetti.y + 10; // The 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);
}
Copy the code
Does it smell like anything other than color and shape?
Inversion of the special effects
So how do you make this fall more natural, a feeling of falling?
In fact, he was doing a flip effect all the time.
To take them apart is to do a rotational motion around a point, and the whole process is to flip itself and move along a trajectory.
Implementing this effect, actually, I mentioned earlier when implementing a square, implementing a square. A parallelogram can be achieved by satisfying the following three points.
-
You know where a point is
-
Know an Angle
-
I know the length of one side
As far as I can tell, the position of a point is easy to determine, which is our starting point, and then the length of our side is known, and we need an Angle, so as long as our Angle is constantly changing, we can achieve this effect.
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 the quadrilateral
var length = 10;
var x1 = fetti.x;
var y1 = fetti.y;
var x2 = fetti.x + (length * Math.sin(fetti.tiltAngle));// The second point
var y2 = fetti.y + (length * Math.cos(fetti.tiltAngle)); // The 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);
}
Copy the code
This is how we achieve the above effects.
Combination of movement
And then put together what we wrote above to make a complete 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; // The first point
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // The first point
fetti.velocity *= fetti.decay;
fetti.tiltAngle += 0.1 // Keep changing the Angle of the quadrilateral
var length = 10;
var x1 = fetti.x;
var y1 = fetti.y;
var x2 = fetti.x + (length * Math.sin(fetti.tiltAngle));// The second point
var y2 = fetti.y + (length * Math.cos(fetti.tiltAngle)); // The 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);
}
Copy the code
Final shape
If you want to achieve the final state, all you need is multiple chunks, fade away, and random colors!
Set how many frames to disappear. We have two variables, totalTicks and tick, to control how many frames to disappear.
For multiple pieces, we just have to do a for loop.
And random colors, I made a colors list.
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})},Copy the code
See the complete code
Github.com/hua1995116/…
Add some meal
Achieve emoji wars in multiplayer battle mode. In our wechat emoji is not a single point, but a multi-player form, so we can continue to explore, using WebSocket and the combination of colorful pieces.
There are a couple of things to note here. (For space reasons, I won’t go into WebSocket, but I will mention some implementation points.)
- We can go through one
tag
To distinguish between historical and real-time messages - Distinguish between your own message and someone else’s to redirect the confetti.
- Animation is only performed for a single 🎉.
- First zoom in and out of the animation, delay 200ms before the 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.spread: r(45.80),
origin: {
x: rect.left / window.innerWidth,
y: rect.top / window.innerHeight
}
});
}, 200)}}Copy the code
To explore more
In this case, we can use the Web worker to calculate, so as to improve the performance. This is for readers to explore. You can also see the source code of Canvas – Confetti
The last
Look back at my previous highly praised articles, maybe you can harvest more oh!
-
From cracking a design website about front-end watermarking (detailed tutorial) : 790+ thumbs up
-
Front end novice guide I learned from King of Glory: 260+ likes
-
This article unlocks the secret of “file download” : 140+ likes
-
10 cross-domain solutions (with the ultimate trick) : 940+ likes
-
This article to understand the whole process of file upload (1.8w word depth analysis, advanced prerequisite) : 260+ points like
conclusion
❤️ follow + like + collect + comment + forward ❤️, original is not easy, encourage the author to create better articles
Follow public AccountAutumn wind notes
, a focus on the front-end interview, engineering, open source front-end public number
- Follow and reply
resume
Get 100+ sets of excellent resume templates - Follow and reply
Good friend
I invite you to the technical exchange group + interview exchange group, and you can also discuss the practice content with me. - Welcome to attention
Autumn wind notes