I am creating a web application that has an interactive background with particles bouncing around. At all times, the screen has about 200 circular particles and not more than 800 particles. Some of the collisions and effects that are performed on particles are the following prototypes. I wonder if I can improve productivity by using web workers to perform these calculations?
/** * Particles */ Jarvis.prototype.genForegroundParticles = function(options, count){ count = count || this.logoParticlesNum; for (var i = 0; i < count; i++) { this.logoParticles.push(new Particle()); } } Jarvis.prototype.genBackgroundParticles = function(options, count){ count = count || this.backgroundParticlesNum; for (var i = 0; i < count; i++) { this.backgroundParticles.push(new Particle(options)); } } Jarvis.prototype.motion = { linear : function(particle, pIndex, particles){ particle.x += particle.vx particle.y += particle.vy }, normalizeVelocity : function(particle, pIndex, particles){ if (particle.vx - particle.vxInitial > 1) { particle.vx -= 0.05; } else if (particle.vx - particle.vxInitial < -1) { particle.vx += 0.05; } if (particle.vy - particle.vyInitial > 1) { particle.vy -= 0.05; } else if (particle.vx - particle.vxInitial < -1) { particle.vy += 0.05; } }, explode : function(particle, pIndex, particles) { if (particle.isBottomOut()) { particles.splice(pIndex, 1); } else { particle.x += particle.vx; particle.y += particle.vy; particle.vy += 0.1; } if (particles.length === 0){ particles.motion.removeMotion("explode"); this.allowMenu = true; } } } Jarvis.prototype.collision = { boundingBox: function(particle, pIndex, particles){ if (particle.y > (this.HEIGHT - particle.radius) || particle.y < particle.radius) { particle.vy *= -1; } if(particle.x > (this.WIDTH - particle.radius) || particle.x < particle.radius) { particle.vx *= -1; } }, boundingBoxGravity: function(particle, pIndex, particles){ // TODO: FIX GRAVITY TO WORK PROPERLY IN COMBINATION WITH FX AND MOTION if (particle.y > (this.HEIGHT - particle.radius) || particle.y < particle.radius) { particle.vy *= -1; particle.vy += 5; } if(particle.x > (this.WIDTH - particle.radius) || particle.x < particle.radius) { particle.vx *= -1; particle.vx += 5; } }, infinity: function(particle, pIndex, particles){ if (particle.x > this.WIDTH){ particle.x = 0; } if (particle.x < 0){ particle.x = this.WIDTH; } if (particle.y > this.HEIGHT){ particle.y = 0; } if (particle.y < 0) { particle.y = this.HEIGHT; } } } Jarvis.prototype.fx = { link : function(particle, pIndex, particles){ for(var j = pIndex + 1; j < particles.length; j++) { var p1 = particle; var p2 = particles[j]; var particleDistance = getDistance(p1, p2); if (particleDistance <= this.particleMinLinkDistance) { this.backgroundCtx.beginPath(); this.backgroundCtx.strokeStyle = "rgba("+p1.red+", "+p1.green+", "+p1.blue+","+ (p1.opacity - particleDistance / this.particleMinLinkDistance) +")"; this.backgroundCtx.moveTo(p1.x, p1.y); this.backgroundCtx.lineTo(p2.x, p2.y); this.backgroundCtx.stroke(); this.backgroundCtx.closePath(); } } }, shake : function(particle, pIndex, particles){ if (particle.xInitial - particle.x >= this.shakeAreaThreshold){ particle.xOper = (randBtwn(this.shakeFactorMin, this.shakeFactorMax) * 2) % (this.WIDTH); } else if (particle.xInitial - particle.x <= -this.shakeAreaThreshold) { particle.xOper = (randBtwn(-this.shakeFactorMax, this.shakeFactorMin) * 2) % (this.WIDTH); } if (particle.yInitial - particle.y >= this.shakeAreaThreshold){ particle.yOper = (randBtwn(this.shakeFactorMin, this.shakeFactorMax) * 2) % (this.HEIGHT); } else if (particle.yInitial - particle.y <= -this.shakeAreaThreshold) { particle.yOper = (randBtwn(-this.shakeFactorMax, this.shakeFactorMin) * 2) % (this.HEIGHT); } particle.x += particle.xOper; particle.y += particle.yOper; }, radialWave : function(particle, pIndex, particles){ var distance = getDistance(particle, this.center); if (particle.radius >= (this.dim * 0.0085)) { particle.radiusOper = -0.02; } else if (particle.radius <= 1) { particle.radiusOper = 0.02; } particle.radius += particle.radiusOper * particle.radius; }, responsive : function(particle, pIndex, particles){ var newPosX = (this.logoParticles.logoOffsetX + this.logoParticles.particleRadius) + (this.logoParticles.particleDistance + this.logoParticles.particleRadius) * particle.arrPos.x; var newPosY = (this.logoParticles.logoOffsetY + this.logoParticles.particleRadius) + (this.logoParticles.particleDistance + this.logoParticles.particleRadius) * particle.arrPos.y; if (particle.xInitial !== newPosX || particle.yInitial !== newPosY){ particle.xInitial = newPosX; particle.yInitial = newPosY; particle.x = particle.xInitial; particle.y = particle.yInitial; } }, motionDetect : function(particle, pIndex, particles){ var isClose = false; var distance = null; for (var i = 0; i < this.touches.length; i++) { var t = this.touches[i]; var point = { x : t.clientX, y : t.clientY } var d = getDistance(point, particle); if (d <= this.blackhole) { isClose = true; if (d <= distance || distance === null) { distance = d; } } } if (isClose){ if (particle.radius < (this.dim * 0.0085)) { particle.radius += 0.25; } if (particle.green >= 0 && particle.blue >= 0) { particle.green -= 10; particle.blue -= 10; } } else { if (particle.radius > particle.initialRadius) { particle.radius -= 0.25; } if (particle.green <= 255 && particle.blue <= 255) { particle.green += 10; particle.blue += 10; } } }, reverseBlackhole : function(particle, pIndex, particles){ for (var i = 0; i < this.touches.length; i++) { var t = this.touches[i]; var point = { x : t.clientX, y : t.clientY } var distance = getDistance(point, particle); if (distance <= this.blackhole){ var diff = getPointsDifference(point, particle); particle.vx += -diff.x / distance; particle.vy += -diff.y / distance; } } } }
Also, if someone is wondering, I have 3 layers of canvas and I will add a particle rendering function and a clear function for all canvas layers
A background that draws a full radial gradient and particles on the screen.
Canvas menu
Buttons for selecting the overlay of menu buttons (show which menu is active, etc.)
Jarvis.prototype.backgroundDraw = function() { // particles var that = this; this.logoParticles.forEach(function(particle, i){ particle.draw(that.backgroundCtx); that.logoParticles.motion.forEach(function(motionType, motionIndex){ that.motion[motionType].call(that, particle, i, that.logoParticles, "foregroundParticles"); }); that.logoParticles.fx.forEach(function(fxType, fxIndex){ that.fx[fxType].call(that, particle, i, that.logoParticles, "foregroundParticles"); }); that.logoParticles.collision.forEach(function(collisionType, collisionIndex){ that.collision[collisionType].call(that, particle, i, that.logoParticles, "foregroundParticles"); }); }); this.backgroundParticles.forEach(function(particle, i){ particle.draw(that.backgroundCtx); that.backgroundParticles.motion.forEach(function(motionType, motionIndex){ that.motion[motionType].call(that, particle, i, that.backgroundParticles, "backgroundParticles"); }); that.backgroundParticles.fx.forEach(function(fxType, fxIndex){ that.fx[fxType].call(that, particle, i, that.backgroundParticles, "backgroundParticles"); }); that.backgroundParticles.collision.forEach(function(collisionType, collisionIndex){ that.collision[collisionType].call(that, particle, i, that.backgroundParticles, "backgroundParticles"); }); }); } Jarvis.prototype.clearCanvas = function() { switch(this.background.type){ case "radial_gradient": this.setBackgroundRadialGradient(this.background.color1, this.background.color2); break; case "plane_color": this.setBackgroundColor(this.background.red, this.background.green, this.background.blue, this.background.opacity); break; default: this.setBackgroundColor(142, 214, 255, 1); } this.foregroundCtx.clearRect(this.clearStartX, this.clearStartY, this.clearDistance, this.clearDistance); this.middlegroundCtx.clearRect(this.clearStartX, this.clearStartY, this.clearDistance, this.clearDistance); } Jarvis.prototype.mainLoop = function() { this.clearCanvas(); this.backgroundDraw(); this.drawMenu(); window.requestAnimFrame(this.mainLoop.bind(this)); }
Any other optimization tips are welcome. I have read several articles, but I'm not sure how to optimize this code.