/* jCanvaScript Parcycle (jcParcycle) by Alaa-eddine KADDOURI www.emaroc.info a port of Parcycle: by Mr Speaker - www.mrspeaker.net */ // Individual particle function cParticle(){ this.position = Vector.create(); this.direction = Vector.create(); this.size = 0; this.sizeSmall = 0; this.timeToLive = 0; this.colour = []; this.drawColour = ""; this.deltaColour = []; this.sharpness = 0; this.jcObj = null; this.jcGrad = null; } // The particle emitter. function cParticleEmitter(){ // Settable Properties this.maxParticles = 30; this.size = 45; this.sizeRandom = 15; this.positionRandom = Vector.create( 10, 10 ); this.sharpness = 40; this.sharpnessRandom = 10; this.lifeSpan = 9; this.lifeSpanRandom = 7; this.speed = 5; this.speedRandom = 1.5; this.angle = 0; this.angleRandom = 360; this.gravity = Vector.create( 0.4, 0.2 ); this.startColour = [ 250, 218, 68, 1 ]; this.startColourRandom = [ 62, 60, 60, 0 ]; this.finishColour = [ 245, 35, 0, 0 ]; this.finishColourRandom = [ 60, 60, 60, 0 ]; this.position = Vector.create( 200, 120 ); this.particles = []; this.active = true; this.particleCount = 0; this.elapsedTime = 0; this.duration = -1; this.emissionRate = 0; this.emitCounter = 0; this.particleIndex = 0; this.init = function(){ this.emissionRate = this.maxParticles / this.lifeSpan; this.emitCounter = 0; this.particleCount = 0; this.particles.length = 0; }; this.addParticle = function(){ if(this.particleCount == this.maxParticles) { return false; } // Take the next particle out of the particle pool we have created and initialize it var particle = new cParticle(); this.initParticle( particle ); this.particles[ this.particleCount ] = particle; // Increment the particle count this.particleCount++; return true; }; this.initParticle = function( particle ){ var RANDM1TO1 = function(){ return Math.random() * 2 - 1; }; particle.position.x = this.position.x + this.positionRandom.x * RANDM1TO1(); particle.position.y = this.position.y + this.positionRandom.y * RANDM1TO1(); var newAngle = (this.angle + this.angleRandom * RANDM1TO1() ) * ( Math.PI / 180 ); // convert to radians var vector = Vector.create( Math.cos( newAngle ), Math.sin( newAngle ) ); // Could move to lookup for speed var vectorSpeed = this.speed + this.speedRandom * RANDM1TO1(); particle.direction = Vector.multiply( vector, vectorSpeed ); particle.size = this.size + this.sizeRandom * RANDM1TO1(); particle.size = particle.size < 0 ? 0 : ~~particle.size; particle.timeToLive = this.lifeSpan + this.lifeSpanRandom * RANDM1TO1(); particle.sharpness = this.sharpness + this.sharpnessRandom * RANDM1TO1(); particle.sharpness = particle.sharpness > 100 ? 100 : particle.sharpness < 0 ? 0 : particle.sharpness; // internal circle gradient size - affects the sharpness of the radial gradient particle.sizeSmall = ~~( ( particle.size / 200 ) * particle.sharpness ); //(size/2/100) var start = [ this.startColour[ 0 ] + this.startColourRandom[ 0 ] * RANDM1TO1(), this.startColour[ 1 ] + this.startColourRandom[ 1 ] * RANDM1TO1(), this.startColour[ 2 ] + this.startColourRandom[ 2 ] * RANDM1TO1(), this.startColour[ 3 ] + this.startColourRandom[ 3 ] * RANDM1TO1() ]; var end = [ this.finishColour[ 0 ] + this.finishColourRandom[ 0 ] * RANDM1TO1(), this.finishColour[ 1 ] + this.finishColourRandom[ 1 ] * RANDM1TO1(), this.finishColour[ 2 ] + this.finishColourRandom[ 2 ] * RANDM1TO1(), this.finishColour[ 3 ] + this.finishColourRandom[ 3 ] * RANDM1TO1() ]; particle.colour = start; particle.deltaColour[ 0 ] = ( end[ 0 ] - start[ 0 ] ) / particle.timeToLive; particle.deltaColour[ 1 ] = ( end[ 1 ] - start[ 1 ] ) / particle.timeToLive; particle.deltaColour[ 2 ] = ( end[ 2 ] - start[ 2 ] ) / particle.timeToLive; particle.deltaColour[ 3 ] = ( end[ 3 ] - start[ 3 ] ) / particle.timeToLive; }; this.update = function( delta ){ if( this.active && this.emissionRate > 0 ){ var rate = 1 / this.emissionRate; this.emitCounter += delta; while( this.particleCount < this.maxParticles && this.emitCounter > rate ){ this.addParticle(); this.emitCounter -= rate; } this.elapsedTime += delta; if( this.duration != -1 && this.duration < this.elapsedTime ){ this.stopParticleEmitter(); } } this.particleIndex = 0; while( this.particleIndex < this.particleCount ) { var currentParticle = this.particles[ this.particleIndex ]; // If the current particle is alive then update it if( currentParticle.timeToLive > 0 ){ // Calculate the new direction based on gravity currentParticle.direction = Vector.add( currentParticle.direction, this.gravity ); currentParticle.position = Vector.add( currentParticle.position, currentParticle.direction ); currentParticle.timeToLive -= delta; // Update colours based on delta var r = currentParticle.colour[ 0 ] += ( currentParticle.deltaColour[ 0 ] * delta ); var g = currentParticle.colour[ 1 ] += ( currentParticle.deltaColour[ 1 ] * delta ); var b = currentParticle.colour[ 2 ] += ( currentParticle.deltaColour[ 2 ] * delta ); var a = currentParticle.colour[ 3 ] += ( currentParticle.deltaColour[ 3 ] * delta ); // Calculate the rgba string to draw. var draw = []; draw.push("rgba(" + ( r > 255 ? 255 : r < 0 ? 0 : ~~r ) ); draw.push( g > 255 ? 255 : g < 0 ? 0 : ~~g ); draw.push( b > 255 ? 255 : b < 0 ? 0 : ~~b ); draw.push( (a > 1 ? 1 : a < 0 ? 0 : a.toFixed( 2 ) ) + ")"); currentParticle.drawColour = draw.join( "," ); this.particleIndex++; var size = currentParticle.size; var halfSize = size >> 1; var x = ~~currentParticle.position.x; var y = ~~currentParticle.position.y; if (currentParticle.jcObj == null) { var radgrad=jc.rGradient( x + halfSize, y + halfSize, currentParticle.sizeSmall, x + halfSize, y + halfSize, halfSize,[[0,currentParticle.drawColour], [1,'rgba(0,0,0,0)']]); currentParticle.jcObj = jc.rect(x, y, size, size, radgrad, 1).composite('lighter'); } else { currentParticle.jcObj.translateTo(x, y); } } else { if (currentParticle != null && currentParticle.jcObj != null) { currentParticle.jcObj.del(); currentParticle.jcObj = null; } // Replace particle with the last active if( this.particleIndex != this.particleCount - 1 ){ this.particles[ this.particleIndex ] = this.particles[ this.particleCount-1 ]; } this.particleCount--; } } }; this.stopParticleEmitter = function(){ this.active = false; this.elapsedTime = 0; this.emitCounter = 0; }; } /* Vector Helper */ var Vector = { create : function( x, y ){ return { "x" : x || -1, "y" : y || -1 }; }, multiply : function( vector, scaleFactor ){ vector.x *= scaleFactor; vector.y *= scaleFactor; return vector; }, add : function( vector1, vector2 ){ vector1.x += vector2.x; vector1.y += vector2.y; return vector1; } };