<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>Canvas烟花粒子</title><metaname="keywords"content="canvas烟花"/><metaname="description"content="canvas动画/"><linkrel="stylesheet"href="css/style.css"></head><body><canvasid="canvas">Canvas is not supported by your browser.</canvas><scriptsrc="js/index.js"></script></body></html>
// Optionsvar options ={/* Which hue should be used for the first batch of rockets? */startingHue:120,/* How many ticks the script should wait before a new firework gets spawned, if the user is holding down his mouse button. */clickLimiter:5,/* How fast the rockets should automatically spawn, based on ticks */timerInterval:40,/* Show pulsing circles marking the targets? */showTargets:true,/* Rocket movement options, should be self-explanatory */rocketSpeed:2,rocketAcceleration:1.03,/* Particle movement options, should be self-explanatory */particleFriction:0.95,particleGravity:1,/* Minimum and maximum amount of particle spawns per rocket */particleMinCount:25,particleMaxCount:40,/* Minimum and maximum radius of a particle */particleMinRadius:3,particleMaxRadius:5};// Local variablesvar fireworks =[];var particles =[];var mouse ={down:false,x:0,y:0};var currentHue = options.startingHue;var clickLimiterTick =0;var timerTick =0;var cntRocketsLaunched =0;// Helper function for canvas animations
window.requestAnimFrame =(function(){return window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||window.mozRequestAnimationFrame ||function(cb){window.setTimeout(callback,1000/60);}})();// Helper function to return random numbers within a specified rangefunctionrandom(min, max){return Math.random()*(max - min)+ min;}// Helper function to calculate the distance between 2 pointsfunctioncalculateDistance(p1x, p1y, p2x, p2y){var xDistance = p1x - p2x;var yDistance = p1y - p2y;return Math.sqrt(Math.pow(xDistance,2)+ Math.pow(yDistance,2));}// Setup some basic variablesvar canvas = document.getElementById('canvas');var canvasCtx = canvas.getContext('2d');var canvasWidth = window.innerWidth;var canvasHeight = window.innerHeight;// Resize canvas
canvas.width = canvasWidth;
canvas.height = canvasHeight;// Firework classfunctionFirework(sx, sy, tx, ty){// Set coordinates (x/y = actual, sx/sy = starting, tx/ty = target)this.x =this.sx = sx;this.y =this.sy = sy;this.tx = tx;this.ty = ty;// Calculate distance between starting and target pointthis.distanceToTarget =calculateDistance(sx, sy, tx, ty);this.distanceTraveled =0;// To simulate a trail effect, the last few coordinates will be storedthis.coordinates =[];this.coordinateCount =3;// Populate coordinate array with initial datawhile(this.coordinateCount--){this.coordinates.push([this.x,this.y]);}// Some settings, you can adjust them if you'd like to do so.this.angle = Math.atan2(ty - sy, tx - sx);this.speed = options.rocketSpeed;this.acceleration = options.rocketAcceleration;this.brightness =random(50,80);this.hue = currentHue;this.targetRadius =1;this.targetDirection =false;// false = Radius is getting bigger, true = Radius is getting smaller// Increase the rockets launched countercntRocketsLaunched++;};// This method should be called each frame to update the fireworkFirework.prototype.update=function(index){// Update the coordinates arraythis.coordinates.pop();this.coordinates.unshift([this.x,this.y]);// Cycle the target radius (used for the pulsing target circle)if(!this.targetDirection){if(this.targetRadius <8)this.targetRadius +=0.15;elsethis.targetDirection =true;}else{if(this.targetRadius >1)this.targetRadius -=0.15;elsethis.targetDirection =false;}// Speed up the firework (could possibly travel faster than the speed of light) this.speed *=this.acceleration;// Calculate the distance the firework has travelled so far (based on velocities)var vx = Math.cos(this.angle)*this.speed;var vy = Math.sin(this.angle)*this.speed;this.distanceTraveled =calculateDistance(this.sx,this.sy,this.x + vx,this.y + vy);// If the distance traveled (including velocities) is greater than the initial distance// to the target, then the target has been reached. If that's not the case, keep traveling.if(this.distanceTraveled >=this.distanceToTarget){createParticles(this.tx,this.ty);fireworks.splice(index,1);}else{this.x += vx;this.y += vy;}};// Draws the fireworkFirework.prototype.draw=function(){var lastCoordinate =this.coordinates[this.coordinates.length -1];// Draw the rocketcanvasCtx.beginPath();canvasCtx.moveTo(lastCoordinate[0], lastCoordinate[1]);canvasCtx.lineTo(this.x,this.y);canvasCtx.strokeStyle ='hsl('+this.hue +',100%,'+this.brightness +'%)';canvasCtx.stroke();// Draw the target (pulsing circle)if(options.showTargets){canvasCtx.beginPath();canvasCtx.arc(this.tx,this.ty,this.targetRadius,0, Math.PI*2);canvasCtx.stroke();}};// Particle classfunctionParticle(x, y){// Set the starting pointthis.x = x;this.y = y;// To simulate a trail effect, the last few coordinates will be storedthis.coordinates =[];this.coordinateCount =5;// Populate coordinate array with initial datawhile(this.coordinateCount--){this.coordinates.push([this.x,this.y]);}// Set a random angle in all possible directions (radians)this.angle =random(0, Math.PI*2);this.speed =random(1,10);// Add some friction and gravity to the particlethis.friction = options.particleFriction;this.gravity = options.particleGravity;// Change the hue to a random numberthis.hue =random(currentHue -20, currentHue +20);this.brightness =random(50,80);this.alpha =1;// Set how fast the particles decaythis.decay =random(0.01,0.03);}// Updates the particle, should be called each frameParticle.prototype.update=function(index){// Update the coordinates arraythis.coordinates.pop();this.coordinates.unshift([this.x,this.y]);// Slow it down (based on friction)this.speed *=this.friction;// Apply velocity to the particlethis.x += Math.cos(this.angle)*this.speed;this.y += Math.sin(this.angle)*this.speed +this.gravity;// Fade out the particle, and remove it if alpha is low enoughthis.alpha -=this.decay;if(this.alpha <=this.decay){particles.splice(index,1);}}// Draws the particleParticle.prototype.draw=function(){var lastCoordinate =this.coordinates[this.coordinates.length -1];var radius = Math.round(random(options.particleMinRadius, options.particleMaxRadius));// Create a new shiny gradientvar gradient = canvasCtx.createRadialGradient(this.x,this.y,0,this.x,this.y, radius);gradient.addColorStop(0.0,'white');gradient.addColorStop(0.1,'white');gradient.addColorStop(0.1,'hsla('+this.hue +',100%,'+this.brightness +'%,'+this.alpha +')');gradient.addColorStop(1.0,'black');// Draw the gradientcanvasCtx.beginPath();canvasCtx.fillStyle = gradient;canvasCtx.arc(this.x,this.y, radius, Math.PI*2,false);canvasCtx.fill();}// Create a bunch of particles at the given positionfunctioncreateParticles(x, y){var particleCount = Math.round(random(options.particleMinCount, options.particleMaxCount));while(particleCount--){particles.push(newParticle(x, y));}}// Add an event listener to the window so we're able to react to size changes
window.addEventListener('resize',function(e){canvas.width = canvasWidth = window.innerWidth;canvas.height = canvasHeight = window.innerHeight;});// Add event listeners to the canvas to handle mouse interactions
canvas.addEventListener('mousemove',function(e){e.preventDefault();mouse.x = e.pageX - canvas.offsetLeft;mouse.y = e.pageY - canvas.offsetTop;});canvas.addEventListener('mousedown',function(e){e.preventDefault();mouse.down =true;});canvas.addEventListener('mouseup',function(e){e.preventDefault();mouse.down =false;});// Main application / script, called when the window is loadedfunctiongameLoop(){// This function will rund endlessly by using requestAnimationFrame (or fallback to setInterval)requestAnimFrame(gameLoop);// Increase the hue to get different colored fireworks over timecurrentHue +=0.5;// 'Clear' the canvas at a specific opacity, by using 'destination-out'. This will create a trailing effect.canvasCtx.globalCompositeOperation ='destination-out';canvasCtx.fillStyle ='rgba(0, 0, 0, 0.5)';canvasCtx.fillRect(0,0, canvasWidth, canvasHeight);canvasCtx.globalCompositeOperation ='lighter';// Loop over all existing fireworks (they should be updated & drawn)var i = fireworks.length;while(i--){fireworks[i].draw();fireworks[i].update(i);}// Loop over all existing particles (they should be updated & drawn)var i = particles.length;while(i--){particles[i].draw();particles[i].update(i);}// Draw some textcanvasCtx.fillStyle ='white';canvasCtx.font ='14px Arial';canvasCtx.fillText('Rockets launched: '+ cntRocketsLaunched,10,24);// Launch fireworks automatically to random coordinates, if the user does not interact with the sceneif(timerTick >= options.timerInterval){if(!mouse.down){fireworks.push(newFirework(canvasWidth /2, canvasHeight,random(0, canvasWidth),random(0, canvasHeight /2)));timerTick =0;}}else{timerTick++;}// Limit the rate at which fireworks can be spawned by mouseif(clickLimiterTick >= options.clickLimiter){if(mouse.down){fireworks.push(newFirework(canvasWidth /2, canvasHeight, mouse.x, mouse.y));clickLimiterTick =0;}}else{clickLimiterTick++;}}window.onload =gameLoop();