JavaScript animation example: fireworks bloom to welcome the new year

Time:2020-10-16

First write an animation effect of fireworks bloom.

When setting off fireworks, a fireworks can be divided into two stages: (1) the fireworks rise into the air; (2) the fireworks explode into pieces, and the exploded fragments slowly dissipate.

Two object classes are abstracted: firework and particle. Firework is used to represent a fireworks object, and particle is used to represent the fragments of a fireworks explosion.

Firework object class defines six attributes: the coordinates (x, y) of each point in the rising trajectory of fireworks, the deflection angle angle of the arc trajectory of fireworks, the horizontal and vertical displacement modifiers XSPEED and yspeed, and the color hue of fireworks.

The initial value of coordinate attribute value y is the height of canvas, which indicates that fireworks rise from the ground to the air. The initial values of other attributes are determined by random numbers. The specific definitions are as follows:

   function Firework()

   {

      this.x = canvas.width/4*(1+3*Math.random());

      this.y = canvas.height – 15;

      this.angle = Math.random() * Math.PI / 4 – Math.PI / 6;

      this.xSpeed = Math.sin(this.angle) *(6+Math.random()*7);

      this.ySpeed = -Math.cos(this.angle) *(6+Math.random()*7);

      this.hue = Math.floor(Math.random() * 360);

   }

Firework object class defines three methods: draw() to draw the rising trajectory of fireworks, update() to change coordinates when fireworks rise, and explode() to explode fireworks. When drawing fireworks trajectory, draw a small filled rectangle with width of 5 and height of 15 at each point (x, y) to represent a trajectory point. When the fireworks rise, the initial value of the vertical speed yspeed is negative. When the value of yspeed is greater than 0, the fireworks rise to the top (can not rise again), and explode into 70 pieces. The implementation of the specific method is shown in the following HTML file content.

The particle object class defines eight attributes: the coordinates (x, y) of each point in the fragment scattered track, the deflection angle angle of the fragment arc track, the horizontal and vertical displacement variables XSPEED and yspeed, the color hue of the fragment, the radius size of the fragment circle, and the brightness of the fragment.

   function Particle(x,y,hue)

   {

      this.x = x;

      this.y = y;

      this.hue = hue;

      this.lightness = 50;

      this.size = 15 + Math.random() * 10;

      this.angle = Math.random() * 2 * Math.PI;

      this.xSpeed = Math.cos(this.angle) *(1+Math.random() * 6);

      this.ySpeed = Math.sin(this.angle) *(1+Math.random() * 6);

   }

The particle object class defines two methods: draw () to draw the trajectory of fragments scattered, and update () to change the coordinates when the fragments are scattered. When the size value is less than 1, the fragment is deleted from the fragment array, indicating that the fragment has died.

Define two arrays, VAR fireworks = []; and VaR particles =]; to store fireworks objects and exploded fragment objects, respectively.

In the function loop of simulating animation, a fireworks object is added to the firework array every time (realized by count count). After the fireworks object rises to the top and explodes, the object element is deleted from the firework array, and then 70 fragment objects are added to the particles array.

Traverse the objects of the two arrays and call their draw () and update () methods respectively.

The complete HTML file content is as follows.

Fireworks bloom 





   var canvas=document.getElementById('myCanvas');
   ctx= canvas.getContext('2d');
   var fireworks=[];
   var particles=[];
   var counter = 0;

   function Firework()
   {
      this.x = canvas.width/4*(1+3*Math.random());
      this.y = canvas.height - 15;
      this.angle = Math.random() * Math.PI / 4 - Math.PI / 6;
      this.xSpeed = Math.sin(this.angle) *(6+Math.random()*7);
      this.ySpeed = -Math.cos(this.angle) *(6+Math.random()*7);
      this.hue = Math.floor(Math.random() * 360);
   }
   Firework.prototype.draw= function() 
   {
      ctx.save();
      ctx.translate(this.x, this.y);
      ctx.rotate(Math.atan2(this.ySpeed, this.xSpeed) + Math.PI / 2);
      ctx.fillStyle =`hsl(${this.hue}, 100%, 50%)`;
      ctx.fillRect(0, 0, 5, 15);
      ctx.restore();
   }
   Firework.prototype.update= function() 
   {
      this.x = this.x + this.xSpeed;
      this.y = this.y + this.ySpeed;
      this.ySpeed += 0.1;
   }
   Firework.prototype.explode= function() 
   {
        for (var i = 0; i < 70; i++) 
       {
          particles.push(new Particle(this.x, this.y, this.hue));
       }
   }

   function Particle(x,y,hue) 
   {
      this.x = x;
      this.y = y;
      this.hue = hue;
      this.lightness = 50;
      this.size = 15 + Math.random() * 10;
      this.angle = Math.random() * 2 * Math.PI;
      this.xSpeed = Math.cos(this.angle) *(1+Math.random() * 6);
      this.ySpeed = Math.sin(this.angle) *(1+Math.random() * 6);
   }
   Particle.prototype.draw= function() 
   {
       ctx.fillStyle = `hsl(${this.hue}, 100%, ${this.lightness}%)`;
       ctx.beginPath();
       ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
       ctx.closePath();
       ctx.fill();
   }
   Particle.prototype.update= function(index) 
   {
       this.ySpeed += 0.05;
       this.size = this.size*0.95;
       this.x = this.x + this.xSpeed;
       this.y = this.y + this.ySpeed;
       if (this.size<1) 
       {
           particles.splice(index,1);
       }
   }
   function loop() 
   {
      ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
      ctx.fillRect(0,0,canvas.width,canvas.height);
      counter++;
      if (counter==15) 
      {
          fireworks.push(new Firework());
          counter=0;
      }
      var i=fireworks.length;
      while (i--) 
      {
          fireworks[i].draw();
          fireworks[i].update();
          if (fireworks[i].ySpeed > 0) 
          {
              fireworks[i].explode();
              fireworks.splice(i, 1);
          }
      }
      var i=particles.length;
      while (i--) 
      {      
          particles[i].draw();
          particles[i].update(i);
      }
      requestAnimationFrame(loop);
   }
  loop();

Open the HTML file containing this HTML code in the browser, and you can see the fireworks bloom animation effect as shown in Figure 1 in the browser window.

 

Picture 1 fireworks bloom

To achieve the effect of fireworks blooming, we can also continue to let the fireworks fragments in a certain area compose “Happy New Year” particle text. See the article for the implementation method of particle textJavaScript animation example: particle text(https://www.cnblogs.com/cs-whut/p/13334882.html)。

Write the following HTML code.

JavaScript animation example: fireworks bloom to welcome the new yearJavaScript animation example: fireworks bloom to welcome the new year

Fireworks in the new year 

  body { margin: 0;  background: black; }
  canvas { position: absolute; }







   function Particle(x, y, hue)
   {
      this.x = x;
      this.y = y;
      this.hue = hue;
      this.lightness = 50;
      this.size = 15 + Math.random() * 10;
      this.angle = Math.random() * 2 * Math.PI;
      this.xSpeed = Math.cos(this.angle) * (1 + Math.random() * 6);
      this.ySpeed = Math.sin(this.angle) * (1 + Math.random() * 6);
      this.target = getTarget();
      this.timer = 0;
   }
   Particle.prototype.draw= function() 
   {
      ctx2.fillStyle =`hsl(${this.hue}, 100%, ${this.lightness}%)`;
      ctx2.beginPath();
      ctx2.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
      ctx2.closePath();
      ctx2.fill();
   }
   Particle.prototype.update= function(idx) 
   {
      if (this.target) 
      {
          var dx = this.target.x - this.x;
          var dy = this.target.y - this.y;
          var dist = Math.sqrt(dx * dx + dy * dy);
          var a = Math.atan2(dy, dx);
          var tx = Math.cos(a) * 5;
          var ty = Math.sin(a) * 5;
          this.size = lerp(this.size, 1.5, 0.05);
          if (dist < 5) 
          {
             this.lightness = lerp(this.lightness, 100, 0.01);
             this.xSpeed = this.ySpeed = 0;
             this.x = lerp(this.x, this.target.x + fidelity / 2, 0.05);
             this.y = lerp(this.y, this.target.y + fidelity / 2, 0.05);
             this.timer += 1;
          }
          else if (dist < 10) 
          {
             this.lightness = lerp(this.lightness, 100, 0.01);
             this.xSpeed = lerp(this.xSpeed, tx, 0.1);
             this.ySpeed = lerp(this.ySpeed, ty, 0.1);
             this.timer += 1;
          } 
          else
          {
             this.xSpeed = lerp(this.xSpeed, tx, 0.02);
             this.ySpeed = lerp(this.ySpeed, ty, 0.02);
          }
      } 
      else
      {
          this.ySpeed += 0.05;
          this.size = this.size*0.95;
          if (this.size<1) 
          {
              particles.splice(idx,1);
          }
      }
      this.x = this.x + this.xSpeed;
      this.y = this.y + this.ySpeed;
   }

   function Firework() 
   {
      this.x = canvas2.width*(1+ 3*Math.random())/4;
      this.y = canvas2.height - 15;
      this.angle = Math.random() * Math.PI / 4 - Math.PI / 6;
      this.xSpeed = Math.sin(this.angle) * (6 + Math.random() * 7);
      this.ySpeed = -Math.cos(this.angle) * (6 + Math.random() * 7);
      this.hue = Math.floor(Math.random() * 360);
   }
   Firework.prototype.draw= function() 
   {
      ctx2.save();
      ctx2.translate(this.x, this.y);
      ctx2.rotate(Math.atan2(this.ySpeed, this.xSpeed) + Math.PI / 2);
      ctx2.fillStyle = `hsl(${this.hue}, 100%, 50%)`;
      ctx2.fillRect(0, 0, 5, 15);
      ctx2.restore();
   }
   Firework.prototype.update= function() 
   {
      this.x = this.x + this.xSpeed;
      this.y = this.y + this.ySpeed;
      this.ySpeed += 0.1;
   }
   Firework.prototype.explode= function() 
   {
      for (var i = 0; i < 70; i++) 
      {
         particles.push(new Particle(this.x, this.y, this.hue));
      }
   }

   function  lerp(a, b, t)
   {
       return Math.abs(b - a)> 0.1 ? a + t * (b - a) : b;
   }
   function getTarget() 
   {
       if (targets.length > 0) 
       {
           var idx = Math.floor(Math.random() * targets.length);
           var { x, y } = targets[idx];
           targets.splice(idx, 1);
           x += canvas2.width / 2 - textWidth / 2;
           y += canvas2.height / 2 - fontSize / 2;
           return { x, y };
       }
   }

   var canvas1=document.getElementById('myCanvas1');
   ctx1= canvas1.getContext('2d');
   var canvas2=document.getElementById('myCanvas2');
   ctx2= canvas2.getContext('2d');
   var canvas3=document.getElementById('myCanvas3');
   ctx3= canvas3.getContext('2d');
   var fontSize = 200;
   var fireworks = [];
   var particles = [];
   var targets = [];
   var fidelity = 3;
   var counter = 0;
   canvas2.width = canvas3.width = window.innerWidth;
   canvas2.height = canvas3.height = window.innerHeight;
   ctx1.fillStyle = '#000';
   var text = 'Happy New Year';
   var textWidth = 999999;
   while (textWidth > window.innerWidth) 
   {
      ctx1.font = `900 ${fontSize--}px Arial`;
      textWidth = ctx1.measureText(text).width;
   }
   canvas1.width = textWidth;
   canvas1.height = fontSize * 1.5;
   ctx1.font = `900 ${fontSize}px Arial`;
   ctx1.fillText(text, 0, fontSize);
   var imgData = ctx1.getImageData(0, 0, canvas1.width, canvas1.height);
   for (var i = 0, max = imgData.data.length; i < max; i += 4) 
   {
       var alpha = imgData.data[i + 3];
       var x = Math.floor(i / 4) % imgData.width;
       var y = Math.floor(i / 4 / imgData.width);
       if (alpha && x % fidelity === 0 && y % fidelity === 0) 
       {
           targets.push({ x, y });
       }
   }
   ctx3.fillStyle = '#FFF';
   ctx3.shadowColor = '#FFF';
   ctx3.shadowBlur = 25;

   function loop() 
   {
      ctx2.fillStyle = "rgba(0, 0, 0, .1)";
      ctx2.fillRect(0, 0, canvas2.width, canvas2.height);
      counter += 1;
      if (counter==15) 
      {
          fireworks.push(new Firework());
          counter=0;
      }
      var i=fireworks.length;
      while (i--) 
      {
          fireworks[i].draw();
          fireworks[i].update();
          if (fireworks[i].ySpeed > 0) 
          {
              fireworks[i].explode();
              fireworks.splice(i, 1);
          }
      }
      var i=particles.length;
      while (i--) 
      {      
         particles[i].draw();
         particles[i].update(i);
         if (particles[i].timer >= 100 || particles[i].lightness >= 99) 
         {
             ctx3.fillRect(particles[i].target.x, particles[i].target.y, fidelity + 1, fidelity + 1);
             particles.splice(i, 1);
         }
      }
      requestAnimationFrame(loop);
   }
   loop();

View Code

Open the HTML file containing this HTML code in the browser, and you can see the animation effect of fireworks blooming to welcome the new year in the browser window as shown in Figure 2. In Figure 2, in order to control the size of the image, a large number of intermediate frames are deleted, so the effect is different from the actual operation.

 

Picture 2 fireworks blooming in the new year

Recommended Today

The selector returned by ngrx store createselector performs one-step debugging of fetching logic

Test source code: import { Component } from ‘@angular/core’; import { createSelector } from ‘@ngrx/store’; export interface State { counter1: number; counter2: number; } export const selectCounter1 = (state: State) => state.counter1; export const selectCounter2 = (state: State) => state.counter2; export const selectTotal = createSelector( selectCounter1, selectCounter2, (counter1, counter2) => counter1 + counter2 ); // […]