An alternative way for using html tags to simulate simple firework effect

Posted on

Problem

I have the following code to simulate a firework (of course a very bad and rough one)

let width = window.innerWidth;
  let height = window.innerHeight;
  let [fire, parts] = [[],[]];
  let updateinterval;
    (function(){
    let canvas = document.querySelector('canvas')
    canvas.width =  width
    canvas.height = height
    let context = canvas.getContext('2d')
    let background =  context.createLinearGradient(0,0,0,height)
    background.addColorStop(0,'darkblue')
    background.addColorStop(0.3,'rgb(75,0,130)')
    background.addColorStop(1,'black')
    context.fillStyle = background;
          context.fillRect(0, 0, width, height);
          
       for(let y = 0;y<200;y++){      
           let size = Math.random()*8;
           let posX = Math.random()*(width-20)
           let posY = Math.random()*(height-20)
           context.beginPath()
           context.fillStyle = 'white'
           context.fillRect(posX,posY,size,size)
        }
           context.fill()
    })();


class part {
  constructor(x, y) {
    this.x = x
    this.y = y;
    this.opacity = 1;
    this.speed = Math.random()*5+1
    this.angle = Math.random() * 2 * Math.PI;
    this.vx = Math.cos(this.angle) * this.speed;
    this.vy = -Math.sin(this.angle) * this.speed;
    this.time = 700;
    this.downtime = 20;
    this.element = document.createElement('div');

    this.element.className = "particle"
    this.element.style.left = this.x + 'px';
    this.element.style.top = this.y + 'px';
    this.element.style.backgroundColor = randomcolor()
    document.body.appendChild(this.element);
    setTimeout(() => {
     parts.splice(parts.indexOf(this), 1);
        this.interval = setInterval(()=>this.down(),this.downtime);

        setTimeout(() =>{
            window.clearInterval(this.interval)
            this.element.remove();
        },this.time)
    }, 900);
  }
  
  down(){

    this.x+=this.vx*0.5
    this.element.style.left = this.x + 'px';
    this.y +=5
    this.element.style.top = this.y + 'px';
    this.opacity -=0.02
    this.element.style.opacity =this.opacity
  }
  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.element.style.left = this.x + 'px';
    this.element.style.top = this.y + 'px';
  }

}

class firework {
  constructor() {
    this.x = width / 2;
    this.y = height
    this.number = Math.random()* 50+150;
    this.speed = 10;
    this.angle = (Math.random() * Math.PI) / 2 + Math.PI / 4;
    this.vx = Math.cos(this.angle) * this.speed;
    this.vy = 1.15*(-Math.sin(this.angle) * this.speed);
    this.element = document.createElement('div');
    this.element.className = 'fire';
    this.element.style.left = this.x + 'px';
    this.element.style.top = this.y + 'px';
    document.body.appendChild(this.element);

    setTimeout(() => {
    for (let y = 0;y < this.number;y++) {
      let particle = new part(this.x, this.y);
      parts.push(particle);
    }
      this.element.remove();
      fire.splice(fire.indexOf(this), 1);
    },700);
  }

  update() {
    this.x += 2*this.vx;
    this.y += 2*this.vy;
    this.vy += 0.2;
    this.element.style.left = this.x + 'px';
    this.element.style.top = this.y + 'px';
  }
}

setInterval(() => {
    let fireworks = new firework();
    fire.push(fireworks);
}, 1000);

setInterval(() => {
  fire.forEach(item => item.update());
  parts.forEach(item =>item.update());
}, 20);

function randomcolor(){
let color = ["rgba(245, 245, 245,1)","rgba(255,255,0,1)","rgba(0,255,255,1)","rgba(147, 250, 165,1)","rgba(0, 230, 64,1)","rgba(127,255,0,1)","rgba(0,255,255,1)","rgba(255, 140, 0,1)","rgba(255,0,255,1)","rgbq(0,255,0,1)","rgba(245,255,250,1)","rgba(255,0,255,1)","rgba(255, 250, 205,1)","rgba(255, 20, 147,1)","rgba(255, 248, 220,1)","rgba(127, 255, 212,1)","rgba(0, 255, 255,1)","rgba(240, 255, 255,1)","rgba(255,0,0,1)", "gold" ]
let ran = Math.floor(Math.random()* color.length)
return color[ran]
}
body{
    margin: 0;
}
canvas{
    opacity: 0.9;
}
.fire{
    position: fixed;
    background-color: white;
    width: 10px;
    z-index: 999;
    height: 10px;
}
.particle{
    /*background-color: red;
    position: absolute;
    width: 5px;
    height: 5px;
    border-radius: 50%;*/
        position: absolute;
    width: 5px;
    height: 5px;
    border-radius: 50%;
}
.line{
     position: absolute;
    width: 2px ;
    height: 20px;
    box-shadow: 0px 0px 5px #fff;
     /*box-shadow: 0px 0px 30px 2px #cf5*/
}
<canvas></canvas>

The code works fine, but it off course a very bad solution to fix this. I used the way of creating and deleting html element to make the particles (hundreds of it) and this will be very slow and have a very high possibility of crashing some computers’ browser.

My problem:

  1. Very bad performance time and may crash browsers
  2. Try to use window.innerHeight and window.innerWidth; to let it work for all resolution/screen size, but this currently only works for some specific screen size
  3. Try to make the particles first concentrate to be the center of the firework, then explode, but fail to do that.

So my major problem is how to make the code execute more effectively (I don’t know the rule of code review very well, but if the second and third violate the rules, please ignore it.)

Thanks for all the response!

Solution

Welcome to CR, a short review;

  • As @Blindman67 mentions, the canvas is a more appropriate medium for this exercise
  • However, this creates at most 200 dom elements, which will never crash anything, nothing even a (modern) phone
  • Some minor performance tricks
    • You divide several times by 2, the fastest way to do that is shift to the right (this.x = width >> 1;)
    • You multiply several times by 2, the fastest way to do that is shift to the left (Math.random() << 1)
    • I would store a reference to the style object on the particle object, because this.style is always faster than this.element.style
    • jsperf says that it is faster to string interpolate this.y + 'px'
    • let color should be declared outside of that function
  • Ideally your code is terminated with ;
  • Ideally your code follows lowerCamelCase, so randomcolor -> randomColor
  • Go for either fully spelled, or Spartan, but not half written words like ran for random or part for particle especially in these 2 cases because ran and part are actual English words
  • Constructor names should start with an uppercase, part -> particle -> Particle

Leave a Reply

Your email address will not be published. Required fields are marked *