firework.js (6508B)
1/* REF: https://fireworks.js.org/ */ 2 3window.requestAnimFrame = ( function() { 4 return window.requestAnimationFrame || 5 window.webkitRequestAnimationFrame || 6 window.mozRequestAnimationFrame || 7 function(callback) { 8 window.setTimeout(callback, 1000 / 60); 9 }; 10})(); 11 12var canvas = document.getElementById('canvas'), 13ctx = canvas.getContext('2d'), 14fireworks = [], 15particles = [], 16hue = 120, 17limiterTotal = 5, 18limiterTick = 0, 19timerTotal = 80, 20timerTick = 0, 21mousedown = false, 22mx, 23my; 24 25function random( min, max ) { 26 return Math.random() * ( max - min ) + min; 27} 28 29function calculateDistance( p1x, p1y, p2x, p2y ) { 30 var xDistance = p1x - p2x, 31 yDistance = p1y - p2y; 32 return Math.sqrt( Math.pow( xDistance, 2 ) + Math.pow( yDistance, 2 ) ); 33} 34 35function Firework( sx, sy, tx, ty ) { 36 // actual coordinates 37 this.x = sx; 38 this.y = sy; 39 // starting coordinates 40 this.sx = sx; 41 this.sy = sy; 42 // target coordinates 43 this.tx = tx; 44 this.ty = ty; 45 // distance from starting point to target 46 this.distanceToTarget = calculateDistance( sx, sy, tx, ty ); 47 this.distanceTraveled = 0; 48 // track the past coordinates of each firework to create a trail 49 // effect, increase the coordinate count to create more prominent trails 50 this.coordinates = []; 51 this.coordinateCount = 3; 52 // populate initial coordinate collection with the current coordinates 53 while( this.coordinateCount-- ) { 54 this.coordinates.push( [ this.x, this.y ] ); 55 } 56 this.angle = Math.atan2( ty - sy, tx - sx ); 57 this.speed = 2; 58 this.acceleration = 1.05; 59 this.brightness = random( 50, 70 ); 60 // circle target indicator radius 61 this.targetRadius = 1; 62} 63 64Firework.prototype.update = function( index ) { 65 // remove last item in coordinates array 66 this.coordinates.pop(); 67 // add current coordinates to the start of the array 68 this.coordinates.unshift( [ this.x, this.y ] ); 69 70 // cycle the circle target indicator radius 71 if( this.targetRadius < 8 ) { 72 this.targetRadius += 0.3; 73 } else { 74 this.targetRadius = 1; 75 } 76 77 // speed up the firework 78 this.speed *= this.acceleration; 79 80 // get the current velocities based on angle and speed 81 var vx = Math.cos( this.angle ) * this.speed, 82 vy = Math.sin( this.angle ) * this.speed; 83 // how far will the firework have traveled with velocities applied? 84 this.distanceTraveled = calculateDistance( this.sx, this.sy, this.x + vx, this.y + vy ); 85 86 // if the distance traveled, including velocities, is greater than the 87 // initial distance to the target, then the target has been reached 88 if( this.distanceTraveled >= this.distanceToTarget ) { 89 createParticles( this.tx, this.ty ); 90 // remove the firework, use the index passed into the update function 91 // to determine which to remove 92 fireworks.splice( index, 1 ); 93 } else { 94 // target not reached, keep traveling 95 this.x += vx; 96 this.y += vy; 97 } 98} 99 100Firework.prototype.draw = function() { 101 ctx.beginPath(); 102 // move to the last tracked coordinate in the set, then draw a line to the current x and y 103 ctx.moveTo( this.coordinates[ this.coordinates.length - 1][ 0 ], this.coordinates[ this.coordinates.length - 1][ 1 ] ); 104 ctx.lineTo( this.x, this.y ); 105 ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)'; 106 ctx.stroke(); 107 108 ctx.beginPath(); 109 // draw the target for this firework with a pulsing circle 110 ctx.arc( this.tx, this.ty, this.targetRadius, 0, Math.PI * 2 ); 111 ctx.stroke(); 112} 113 114function Particle( x, y ) { 115 this.x = x; 116 this.y = y; 117 // track the past coordinates of each particle to create a trail effect, increase the coordinate count to create more prominent trails 118 this.coordinates = []; 119 this.coordinateCount = 5; 120 while( this.coordinateCount-- ) { 121 this.coordinates.push( [ this.x, this.y ] ); 122 } 123 // set a random angle in all possible directions, in radians 124 this.angle = random( 0, Math.PI * 2 ); 125 this.speed = random( 1, 10 ); 126 // friction will slow the particle down 127 this.friction = 0.95; 128 // gravity will be applied and pull the particle down 129 this.gravity = 1; 130 // set the hue to a random number +-50 of the overall hue variable 131 this.hue = random( hue - 50, hue + 50 ); 132 this.brightness = random( 50, 80 ); 133 this.alpha = 1; 134 // set how fast the particle fades out 135 this.decay = random( 0.015, 0.03 ); 136} 137 138Particle.prototype.update = function( index ) { 139 // remove last item in coordinates array 140 this.coordinates.pop(); 141 // add current coordinates to the start of the array 142 this.coordinates.unshift( [ this.x, this.y ] ); 143 // slow down the particle 144 this.speed *= this.friction; 145 // apply velocity 146 this.x += Math.cos( this.angle ) * this.speed; 147 this.y += Math.sin( this.angle ) * this.speed + this.gravity; 148 // fade out the particle 149 this.alpha -= this.decay; 150 151 // remove the particle once the alpha is low enough, based on the passed in index 152 if( this.alpha <= this.decay ) { 153 particles.splice( index, 1 ); 154 } 155} 156 157Particle.prototype.draw = function() { 158 ctx. beginPath(); 159 // move to the last tracked coordinates in the set, then draw a line to the current x and y 160 ctx.moveTo( this.coordinates[ this.coordinates.length - 1 ][ 0 ], this.coordinates[ this.coordinates.length - 1 ][ 1 ] ); 161 ctx.lineTo( this.x, this.y ); 162 ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')'; 163 ctx.stroke(); 164} 165 166function createParticles( x, y ) { 167 var particleCount = 30; 168 while( particleCount-- ) { 169 particles.push( new Particle( x, y ) ); 170 } 171} 172 173function firework_loop() { 174 requestAnimFrame(firework_loop); 175 176 canvas.width = canvas.clientWidth 177 canvas.height = canvas.clientHeight 178 179 hue = random(0, 360 ); 180 181 // normally, clearRect() would be used to clear the canvas 182 // we want to create a trailing effect though 183 // setting the composite operation to destination-out will allow us to 184 // clear the canvas at a specific opacity, rather than wiping it entirely 185 ctx.globalCompositeOperation = 'destination-out'; 186 // decrease the alpha property to create more prominent trails 187 ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; 188 ctx.fillRect(0, 0, canvas.clientWidth, canvas.clientWidth); 189 // change the composite operation back to our main mode 190 // lighter creates bright highlight points as the fireworks and particles overlap each other 191 ctx.globalCompositeOperation = 'lighter'; 192 193 // loop over each firework, draw it, update it 194 var i = fireworks.length; 195 while( i-- ) { 196 fireworks[ i ].draw(); 197 fireworks[ i ].update( i ); 198 } 199 200 // loop over each particle, draw it, update it 201 var i = particles.length; 202 while( i-- ) { 203 particles[ i ].draw(); 204 particles[ i ].update( i ); 205 } 206}