1
\$\begingroup\$

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!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Use the canvas to render particles as it is much quicker than moving elements. Even quicker would be to use WebGL \$\endgroup\$
    – Blindman67
    Jan 31, 2022 at 17:21

1 Answer 1

2
\$\begingroup\$

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
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the answer, I will try to use canvas, just one question, about how many dom elements will crash the browser and what is a safe amount of them. 200 with updating every time seems slow the browser on phone. Also, don't people find it hard to use uppercase for variable for coding? Do you mind kindly share your advice ?I am new to coding.... \$\endgroup\$
    – James
    Feb 13, 2022 at 20:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.