Skip to content

Commit

Permalink
add optional stretch effect. rockets now appear as streaks instead of…
Browse files Browse the repository at this point in the history
… a series of dots. tweak explosion parameters for a more spectacular show.
  • Loading branch information
skeltoac committed Dec 16, 2011
1 parent 70439e3 commit 91a05d6
Showing 1 changed file with 97 additions and 42 deletions.
139 changes: 97 additions & 42 deletions js/canvas.js
Expand Up @@ -52,7 +52,8 @@
step : 0, // one per timeline event step : 0, // one per timeline event
timer : null, // from setInterval timer : null, // from setInterval
stopped : false, stopped : false,
targetParticleDensity : 1.5, maxParticleDensity : 1.5,
minParticleDensity : 0.5,
particleDensity : 1 particleDensity : 1
}; };


Expand Down Expand Up @@ -266,28 +267,31 @@


Fireworks.prototype.launchRocket = function(idx) { Fireworks.prototype.launchRocket = function(idx) {
opts={ opts={
scale: Math.round(Math.random() * 3) / 6, scale: Math.round(Math.random() * 2) / 8,
stretch: true,
pos: new Vector3(Math.random() * 100 - 50, Math.random() * 3 + 190, Math.random() * 4 - 2), pos: new Vector3(Math.random() * 100 - 50, Math.random() * 3 + 190, Math.random() * 4 - 2),
vel: new Vector3(Math.random() * 12 - 6, Math.random() * 6 - 20, Math.random() * 6 - 3), vel: new Vector3(Math.random() * 12 - 6, Math.random() * 6 - 20, Math.random() * 6 - 3),
img: this.imgs.rocket.random(), img: this.imgs.rocket.random(),
data: this.timeline[ this.step ][ idx ], data: this.timeline[ this.step ][ idx ],
expendable: false, expendable: false,
cont: function(particle) { cont: function(particle) {
// Continue while rising // Continue while rising
if ( particle.vel.y < 0 ) if ( particle.vel.y < -1.5 )
return true; return true;
this.update(0, true);
// Spawn explosion particles // Spawn explosion particles
particle.stretch = false;
particle.fireworks.explodeCore(particle.pos, particle.data[0]); particle.fireworks.explodeCore(particle.pos, particle.data[0]);
if ( Math.random() < .92 ) if ( Math.random() < .93 )
particle.fireworks.explodeShell(particle.pos, particle.data[1]); particle.fireworks.explodeShell(particle.pos, particle.data[1], particle);
else else
particle.fireworks.explodeRaster('logo', particle.pos, particle.data[0]); particle.fireworks.explodeRaster('logo', particle.pos, particle.data[0]);
particle.fireworks.explodeRing(particle.pos, particle.data[2]); particle.fireworks.explodeRing(particle.pos, particle.data[2], particle);
// Become bright, expand, contract, fade away // Become bright, expand, contract, fade away
particle.img = particle.fireworks.imgs.explosion.random(); particle.img = particle.fireworks.imgs.explosion.random();
particle.vel = new Vector3(0, 0, 0); particle.vel = new Vector3(0, 0, 0);
particle.grav = 0; particle.grav = 0;
var x = Math.max(Math.sqrt(particle.data[0]) / 10, 0.25); var x = Math.max(Math.sqrt(particle.data[0]) / 20, 0.25);
particle.scales = [2,4,5,5,4,4,3,3,2,2,0].map(function(s){return s*x;}); particle.scales = [2,4,5,5,4,4,3,3,2,2,0].map(function(s){return s*x;});
particle.cont = function(particle) { return particle.scale = particle.scales.shift(); }; particle.cont = function(particle) { return particle.scale = particle.scales.shift(); };
return true; return true;
Expand All @@ -296,48 +300,65 @@
this.getParticle(opts); this.getParticle(opts);
}; };


Fireworks.prototype.explodeShell = function(pos, mag) { Fireworks.prototype.explodeShell = function(pos, mag, op) {
if ( mag <= 0 ) if ( mag <= 0 )
return; return;
var root = Math.sqrt(mag) + 1; var root = Math.sqrt(mag) + 1;
var vel = new Vector3(root, root, root); var vel = new Vector3(root, root, root);
var cont = function(p) { return --p.timer > 0 || Math.random() > 0.3; } var cont = function(p) { return --p.timer > 0 || Math.random() > 0.25; }
var scale = 1;
var stretched = false;
var imgs = []; var imgs = [];
imgs[0] = this.imgs.shell.random(); if ( Math.random() > 0.5 ) {
if ( Math.random() > 0.8 ) imgs[0] = this.imgs.shell.random();
imgs[1] = this.imgs.shell.random(); if ( Math.random() > 0.8 )
imgs[1] = this.imgs.shell.random();
scale = (Math.random() / 2) + 0.75;
} else {
imgs[0] = this.imgs.ring.random();
if ( Math.random() > 0.8 )
imgs[1] = this.imgs.shell.random();
scale = (Math.random() / 2) + 0.25;
}
// Spawn a symmetrical pair of particles for each unit magnitude // Spawn a symmetrical pair of particles for each unit magnitude
var numP = this.scaleParticleCount(mag * 3); var numP = this.scaleParticleCount(mag * 3);
for ( var i = 0; i < numP; ++i ) { for ( var i = 0; i < numP; ++i ) {
vel.rotate(Math.random() * 3, Math.random() * 3, Math.random() * 3); vel.rotate(Math.random() * 3, Math.random() * 3, Math.random() * 3);
var myVel = vel.copy().multiplyEq( (Math.random() + 19) / 20); var myVel = vel.copy().multiplyEq( (Math.random() + 19) / 20);
this.getParticle({ this.getParticle({
scale: scale,
pos: pos.copy(), pos: pos.copy(),
vel: myVel, vel: myVel,
grav: 0.4, grav: 0.3,
drag: 0.9, drag: 0.91,
cont: cont, cont: cont,
img: imgs.random(), img: imgs.random(),
timer: 20, timer: 23,
x: op.x,
y: op.y
}); });
this.getParticle({ this.getParticle({
scale: scale,
stretched: stretched,
pos: pos.copy(), pos: pos.copy(),
vel: myVel.invert(), vel: myVel.invert(),
grav: 0.4, grav: 0.3,
drag: 0.9, drag: 0.91,
cont: cont, cont: cont,
img: imgs.random(), img: imgs.random(),
timer: 20, timer: 23,
x: op.x,
y: op.y
}); });
} }
}; };


Fireworks.prototype.explodeRing = function(pos, mag) { Fireworks.prototype.explodeRing = function(pos, mag, op) {
if ( mag <= 0 ) if ( mag <= 0 )
return; return;
var root = Math.sqrt(mag) + 1; var root = Math.sqrt(mag) + 1;
var vel = new Vector3(root, 0, root); var vel = new Vector3(root, 0, root);
var cont = function(p) { return --p.timer > 0 || Math.random() > 0.3; } var cont = function(p) { return --p.timer > 0 || Math.random() > 0.25; }
var rX = 1 - 2 * Math.random(); var rX = 1 - 2 * Math.random();
var rZ = 1 - 2 * Math.random(); var rZ = 1 - 2 * Math.random();
var img = this.imgs.ring.random(); var img = this.imgs.ring.random();
Expand All @@ -346,24 +367,30 @@
vel.rotateY(Math.random() * 3); vel.rotateY(Math.random() * 3);
var myVel = vel.copy().rotateX(rX).rotateZ(rZ).multiplyEq( 1.5 + Math.random() / 5 ); var myVel = vel.copy().rotateX(rX).rotateZ(rZ).multiplyEq( 1.5 + Math.random() / 5 );
this.getParticle({ this.getParticle({
stretch: true,
pos: pos.copy(), pos: pos.copy(),
vel: myVel, vel: myVel,
grav: 0.4, grav: 0.3,
drag: 0.92, drag: 0.91,
cont: cont, cont: cont,
img: img, img: img,
scale: 0.5, scale: 0.5,
timer: 20, timer: 23,
x: op.x,
y: op.y
}); });
this.getParticle({ this.getParticle({
stretch: true,
pos: pos.copy(), pos: pos.copy(),
vel: myVel.invert(), vel: myVel.invert(),
grav: 0.4, grav: 0.3,
drag: 0.92, drag: 0.91,
cont: cont, cont: cont,
img: img, img: img,
scale: 0.5, scale: 0.5,
timer: 20, timer: 23,
x: op.x,
y: op.y
}); });
} }
}; };
Expand All @@ -373,7 +400,7 @@
return; return;
var root = Math.sqrt(mag) / 3; var root = Math.sqrt(mag) / 3;
var vel = new Vector3(root, root, root); var vel = new Vector3(root, root, root);
var cont = function(p) { return --p.timer > 0 || Math.random() > 0.3; } var cont = function(p) { return --p.timer > 0 || Math.random() > 0.25; }
var numP = 5 + this.scaleParticleCount(mag / 20); var numP = 5 + this.scaleParticleCount(mag / 20);
for ( var i = 0; i < numP; ++i ) { for ( var i = 0; i < numP; ++i ) {
vel.rotate(Math.random() * 3, Math.random() * 3, Math.random() * 3); vel.rotate(Math.random() * 3, Math.random() * 3, Math.random() * 3);
Expand All @@ -387,7 +414,7 @@
img: this.imgs.core[0], img: this.imgs.core[0],
imgs: this.imgs.core, imgs: this.imgs.core,
scale: 0.6, scale: 0.6,
timer: 20, timer: 23,
}); });
this.getParticle({ this.getParticle({
pos: pos.copy(), pos: pos.copy(),
Expand All @@ -398,7 +425,7 @@
img: this.imgs.core[0], img: this.imgs.core[0],
imgs: this.imgs.core, imgs: this.imgs.core,
scale: 0.6, scale: 0.6,
timer: 20, timer: 23,
}); });
} }
}; };
Expand All @@ -407,7 +434,7 @@
if ( mag <= 0 ) if ( mag <= 0 )
return; return;
var root = Math.sqrt(mag); var root = Math.sqrt(mag);
var cont = function(p) { return --p.timer > 0 || Math.random() > 0.3; }; var cont = function(p) { return --p.timer > 0 || Math.random() > 0.25; };
// convert raster into array of particles // convert raster into array of particles
var canvas = document.createElement("canvas"); var canvas = document.createElement("canvas");
var c = canvas.getContext("2d"); var c = canvas.getContext("2d");
Expand All @@ -432,7 +459,7 @@
cont: cont, cont: cont,
img: img, img: img,
scale: 1, scale: 1,
timer: 20, timer: 23,
}); });
} }
pCount += this.particleDensity; pCount += this.particleDensity;
Expand Down Expand Up @@ -460,12 +487,17 @@
}; };


Fireworks.prototype.draw3Din2D = function(particle) { Fireworks.prototype.draw3Din2D = function(particle) {
var scale = this.fov / ( this.fov + particle.pos.z ); if ( particle.scale > 0 ) {
var x2d = ( particle.pos.x * scale) + this.canvas.width / 2; var scale = this.fov / ( this.fov + particle.pos.z );
var y2d = ( particle.pos.y * scale) + this.canvas.height / 2; var x2d = ( particle.pos.x * scale) + this.canvas.width / 2;
scale *= 6 * particle.scale; var y2d = ( particle.pos.y * scale) + this.canvas.height / 2;
if (scale > 0) scale *= 6 * particle.scale;
this.context.drawImage(particle.img, x2d - scale, y2d - scale, scale * 2, scale * 2); this.context.save();
this.context.translate( x2d, y2d );
particle.transform(this.context, x2d, y2d, !this.isMouseDown);
this.context.drawImage(particle.img, 0, 0, scale * 2, scale * 2);
this.context.restore();
}
}; };


Fireworks.prototype.render = function() { Fireworks.prototype.render = function() {
Expand Down Expand Up @@ -493,9 +525,9 @@
i += 6; i += 6;
} }
} }
this.particleDensity *= 0.93; this.particleDensity -= (this.particleDensity - this.minParticleDensity) / 50;
} else { } else {
this.particleDensity += (this.targetParticleDensity - this.particleDensity ) / 300; this.particleDensity += (this.maxParticleDensity - this.particleDensity) / 200;
} }
if ( this.tick % this.stepInterval == 0 ) { if ( this.tick % this.stepInterval == 0 ) {
for ( var i = 0; i < this.timeline[this.step].length; ++i ) for ( var i = 0; i < this.timeline[this.step].length; ++i )
Expand All @@ -506,7 +538,7 @@
for (i = 0; i < this.particles.length; i++) for (i = 0; i < this.particles.length; i++)
this.particles[i].update(i); this.particles[i].update(i);
} }
this.context.fillStyle = "rgba(0,0,0,0.3)"; this.context.fillStyle = "rgba(0,0,0,0.25)";
} else { } else {
for (i = 0; i < this.particles.length; i++) { for (i = 0; i < this.particles.length; i++) {
this.particles[i].pos.rotateY((this.lastMouseX - this.mouseX) * 0.01); this.particles[i].pos.rotateY((this.lastMouseX - this.mouseX) * 0.01);
Expand Down Expand Up @@ -612,6 +644,9 @@
enabled: true, enabled: true,
data: null, data: null,
scale: 1, scale: 1,
x: false,
y: false,
stretch: false,
imgs: false, imgs: false,
expendable: true, expendable: true,
cont: function(particle) { cont: function(particle) {
Expand All @@ -623,9 +658,9 @@
$.extend(this, this.defaults, opts); $.extend(this, this.defaults, opts);
}; };


Particle.prototype.update = function(i) { Particle.prototype.update = function(i, cont) {
if (this.enabled) { if (this.enabled) {
if ( this.cont(this) ) { if ( cont || this.cont(this) ) {
this.pos.plusEq(this.vel); this.pos.plusEq(this.vel);
this.vel.multiplyEq((1 - this.fireworks.drag) * this.drag); this.vel.multiplyEq((1 - this.fireworks.drag) * this.drag);
this.vel.y += this.fireworks.gravity * this.grav; this.vel.y += this.fireworks.gravity * this.grav;
Expand All @@ -638,6 +673,26 @@
} }
}; };


Particle.prototype.transform = function(context, x, y, stretch) {
if ( this.x === false ) {
this.x = x;
this.y = y;
}
if ( this.stretch && stretch ) {
var dx = x - this.x;
var dy = y - this.y;
var angle = Math.atan2( dy, dx );
if ( angle < 0 )
angle += Math.PI * 2;
var distance = Math.sqrt( Math.pow( dx, 2 ) + Math.pow( dy, 2 ) );
context.rotate(angle);
context.scale(distance / this.scale / 5, 1);
}
context.translate(- this.scale * 6, - this.scale * 6);
this.x = x;
this.y = y;
};

Particle.prototype.disable = function() { Particle.prototype.disable = function() {
if (this.enabled && this.expendable) { if (this.enabled && this.expendable) {
this.enabled = false; this.enabled = false;
Expand Down

0 comments on commit 91a05d6

Please sign in to comment.