html--烟花3
// Options
var 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 variables
var 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 range
function random(min, max) {
return Math.random() * (max - min) + min;
}
// Helper function to calculate the distance between 2 points
function calculateDistance(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 variables
var 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 class
function Firework(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 point
this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
this.distanceTraveled = 0;
// To simulate a trail effect, the last few coordinates will be stored
this.coordinates = [];
this.coordinateCount = 3;
// Populate coordinate array with initial data
while(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 counter
cntRocketsLaunched++;
};
// This method should be called each frame to update the firework
Firework.prototype.update = function(index) {
// Update the coordinates array
this.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;
else
this.targetDirection = true;
} else {
if(this.targetRadius > 1)
this.targetRadius -= 0.15;
else
this.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 firework
Firework.prototype.draw = function() {
var lastCoordinate = this.coordinates[this.coordinates.length - 1];
// Draw the rocket
canvasCtx.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 class
function Particle(x, y) {
// Set the starting point
this.x = x;
this.y = y;
// To simulate a trail effect, the last few coordinates will be stored
this.coordinates = [];
this.coordinateCount = 5;
// Populate coordinate array with initial data
while(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 particle
this.friction = options.particleFriction;
this.gravity = options.particleGravity;
// Change the hue to a random number
this.hue = random(currentHue - 20, currentHue + 20);
this.brightness = random(50, 80);
this.alpha = 1;
// Set how fast the particles decay
this.decay = random(0.01, 0.03);
}
// Updates the particle, should be called each frame
Particle.prototype.update = function(index) {
// Update the coordinates array
this.coordinates.pop();
this.coordinates.unshift([this.x, this.y]);
// Slow it down (based on friction)
this.speed *= this.friction;
// Apply velocity to the particle
this.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 enough
this.alpha -= this.decay;
if(this.alpha <= this.decay) {
particles.splice(index, 1);
}
}
// Draws the particle
Particle.prototype.draw = function() {
var lastCoordinate = this.coordinates[this.coordinates.length - 1];
var radius = Math.round(random(options.particleMinRadius, options.particleMaxRadius));
// Create a new shiny gradient
var 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 gradient
canvasCtx.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 position
function createParticles(x, y) {
var particleCount = Math.round(random(options.particleMinCount, options.particleMaxCount));
while(particleCount--) {
particles.push(new Particle(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 loaded
function gameLoop() {
// This function will rund endlessly by using requestAnimationFrame (or fallback to setInterval)
requestAnimFrame(gameLoop);
// Increase the hue to get different colored fireworks over time
currentHue += 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 text
canvasCtx.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 scene
if(timerTick >= options.timerInterval) {
if(!mouse.down) {
fireworks.push(new Firework(canvasWidth / 2, canvasHeight, random(0, canvasWidth), random(0, canvasHeight / 2)));
timerTick = 0;
}
} else {
timerTick++;
}
// Limit the rate at which fireworks can be spawned by mouse
if(clickLimiterTick >= options.clickLimiter) {
if(mouse.down) {
fireworks.push(new Firework(canvasWidth / 2, canvasHeight, mouse.x, mouse.y));
clickLimiterTick = 0;
}
} else {
clickLimiterTick++;
}
}
window.onload = gameLoop();