Created
May 30, 2014 13:12
-
-
Save vangog/db0c5f3ad69d3bfa0ac2 to your computer and use it in GitHub Desktop.
Great Firework!
From http://jsfiddle.net/licson0729/8Sgn3/light/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.requestAnimFrame = (function () { | |
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
var canvas = document.getElementById('canvas'), | |
ctx = canvas.getContext('2d'), | |
cw = window.innerWidth, | |
ch = window.innerHeight, | |
fireworks = [], | |
particles = [], | |
hue = 120, | |
limiterTotal = 5, | |
limiterTick = 0, | |
timerTotal = 60, | |
timerTick = 0, | |
mousedown = false, | |
mx, | |
my; | |
var audio = new(window.AudioContext || window.webkitAudioContext)(); | |
var sound, buffer; | |
if (!audio) { | |
var xhr = new XMLHttpRequest(); | |
xhr.responseType = "arraybuffer"; | |
xhr.onload = function () { | |
audio.decodeAudioData(xhr.response, function (b) { | |
buffer = b; | |
}); | |
}; | |
xhr.open("GET", "https://dl.dropboxusercontent.com/u/162276646/Sounds/117616__soundmary__fireworks-exploding-1.wav", true); | |
xhr.send(null); | |
} | |
function playSound() { | |
if (audio) { | |
sound = audio.createBufferSource(); | |
sound.buffer = buffer; | |
sound.connect(audio.destination); | |
sound.playbackRate.value = random(0.6, 1.2); | |
sound.play ? sound.play(0) : sound.noteOn(0); | |
} | |
}; | |
canvas.width = cw; | |
canvas.height = ch; | |
function random(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
function calculateDistance(p1x, p1y, p2x, p2y) { | |
var xDistance = p1x - p2x, | |
yDistance = p1y - p2y; | |
return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2)); | |
} | |
// create firework | |
function Firework(sx, sy, tx, ty) { | |
// actual coordinates | |
this.x = sx; | |
this.y = sy; | |
// starting coordinates | |
this.sx = sx; | |
this.sy = sy; | |
// target coordinates | |
this.tx = tx; | |
this.ty = ty; | |
// distance from starting point to target | |
this.distanceToTarget = calculateDistance(sx, sy, tx, ty); | |
this.distanceTraveled = 0; | |
// track the past coordinates of each firework to create a trail effect, increase the coordinate count to create more prominent trails | |
this.coordinates = []; | |
this.coordinateCount = 3; | |
// populate initial coordinate collection with the current coordinates | |
while (this.coordinateCount--) { | |
this.coordinates.push([this.x, this.y]); | |
} | |
this.angle = Math.atan2(ty - sy, tx - sx); | |
this.speed = 3; | |
this.acceleration = 1.05 | |
this.brightness = random(50, 70); | |
// circle target indicator radius | |
this.targetRadius = 1; | |
} | |
// update firework | |
Firework.prototype.update = function (index) { | |
// remove last item in coordinates array | |
this.coordinates.pop(); | |
// add current coordinates to the start of the array | |
this.coordinates.unshift([this.x, this.y]); | |
// cycle the circle target indicator radius | |
if (this.targetRadius < 8) { | |
this.targetRadius += 0.3; | |
} else { | |
this.targetRadius = 1; | |
} | |
// speed up the firework | |
this.speed *= this.acceleration; | |
// get the current velocities based on angle and speed | |
var vx = Math.cos(this.angle) * this.speed, | |
vy = Math.sin(this.angle) * this.speed; | |
// how far will the firework have traveled with velocities applied? | |
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 (this.distanceTraveled >= this.distanceToTarget) { | |
createParticles(this.tx, this.ty); | |
// remove the firework, use the index passed into the update function to determine which to remove | |
fireworks.splice(index, 1); | |
playSound(); | |
} else { | |
// target not reached, keep traveling | |
this.x += vx; | |
this.y += vy; | |
} | |
} | |
// draw firework | |
Firework.prototype.draw = function () { | |
ctx.beginPath(); | |
// move to the last tracked coordinate in the set, then draw a line to the current x and y | |
ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]); | |
ctx.lineTo(this.x, this.y); | |
ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)'; | |
ctx.stroke(); | |
ctx.beginPath(); | |
// draw the target for this firework with a pulsing circle | |
ctx.arc(this.tx, this.ty, this.targetRadius, 0, Math.PI * 2); | |
ctx.stroke(); | |
} | |
// create particle | |
function Particle(x, y) { | |
this.x = x; | |
this.y = y; | |
// track the past coordinates of each particle to create a trail effect, increase the coordinate count to create more prominent trails | |
this.coordinates = []; | |
this.coordinateCount = 5; | |
while (this.coordinateCount--) { | |
this.coordinates.push([this.x, this.y]); | |
} | |
// set a random angle in all possible directions, in radians | |
this.angle = random(0, Math.PI * 2); | |
this.speed = random(1, 10); | |
// friction will slow the particle down | |
this.friction = 0.95; | |
// gravity will be applied and pull the particle down | |
this.gravity = 1; | |
// set the hue to a random number +-20 of the overall hue variable | |
this.hue = random(hue - 20, hue + 20); | |
this.brightness = random(10, 90); | |
this.alpha = 1; | |
// set how fast the particle fades out | |
this.decay = random(0.006, 0.008); | |
} | |
// update particle | |
Particle.prototype.update = function (index) { | |
// remove last item in coordinates array | |
this.coordinates.pop(); | |
// add current coordinates to the start of the array | |
this.coordinates.unshift([this.x, this.y]); | |
// slow down the particle | |
this.speed *= this.friction; | |
// apply velocity | |
this.x += Math.cos(this.angle) * this.speed; | |
this.y += Math.sin(this.angle) * this.speed + this.gravity; | |
// fade out the particle | |
this.alpha -= this.decay; | |
// remove the particle once the alpha is low enough, based on the passed in index | |
if (this.alpha <= this.decay) { | |
particles.splice(index, 1); | |
} | |
} | |
// draw particle | |
Particle.prototype.draw = function () { | |
ctx.beginPath(); | |
// move to the last tracked coordinates in the set, then draw a line to the current x and y | |
ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]); | |
ctx.lineTo(this.x, this.y); | |
ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')'; | |
ctx.stroke(); | |
} | |
// create particle group/explosion | |
function createParticles(x, y) { | |
// increase the particle count for a bigger explosion, beware of the canvas performance hit with the increased particles though | |
var particleCount = 100; | |
while (particleCount--) { | |
particles.push(new Particle(x, y)); | |
} | |
} | |
// main demo loop | |
function loop() { | |
// this function will run endlessly with requestAnimationFrame | |
requestAnimFrame(loop); | |
// increase the hue to get different colored fireworks over time | |
hue += 0.5; | |
// normally, clearRect() would be used to clear the canvas | |
// we want to create a trailing effect though | |
// setting the composite operation to destination-out will allow us to clear the canvas at a specific opacity, rather than wiping it entirely | |
ctx.globalCompositeOperation = 'destination-out'; | |
// decrease the alpha property to create more prominent trails | |
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; | |
ctx.fillRect(0, 0, cw, ch); | |
// change the composite operation back to our main mode | |
// lighter creates bright highlight points as the fireworks and particles overlap each other | |
ctx.globalCompositeOperation = 'lighter'; | |
// loop over each firework, draw it, update it | |
var i = fireworks.length; | |
while (i--) { | |
fireworks[i].draw(); | |
fireworks[i].update(i); | |
} | |
// loop over each particle, draw it, update it | |
var i = particles.length; | |
while (i--) { | |
particles[i].draw(); | |
particles[i].update(i); | |
} | |
// launch fireworks automatically to random coordinates, when the mouse isn't down | |
if (timerTick >= timerTotal) { | |
if (!mousedown) { | |
// start the firework at the bottom middle of the screen, then set the random target coordinates, the random y coordinates will be set within the range of the top half of the screen | |
for (var i = 0; i < 3; i++) { | |
setTimeout(function () { | |
fireworks.push(new Firework(cw / 2 + random(-cw / 6, cw / 6), ch, random(0, cw), random(0, ch / 2))); | |
}, 250 * i); | |
} | |
timerTick = 0; | |
} | |
} else { | |
timerTick++; | |
} | |
// limit the rate at which fireworks get launched when mouse is down | |
if (limiterTick >= limiterTotal) { | |
if (mousedown) { | |
// start the firework at the bottom middle of the screen, then set the current mouse coordinates as the target | |
fireworks.push(new Firework(cw / 2, ch, mx, my)); | |
limiterTick = 0; | |
} | |
} else { | |
limiterTick++; | |
} | |
} | |
// mouse event bindings | |
// update the mouse coordinates on mousemove | |
canvas.addEventListener('mousemove', function (e) { | |
mx = e.pageX - canvas.offsetLeft; | |
my = e.pageY - canvas.offsetTop; | |
}); | |
// toggle mousedown state and prevent canvas from being selected | |
canvas.addEventListener('mousedown', function (e) { | |
e.preventDefault(); | |
mousedown = true; | |
}); | |
canvas.addEventListener('mouseup', function (e) { | |
e.preventDefault(); | |
mousedown = false; | |
}); | |
loop(); | |
window.onresize = function () { | |
cw = canvas.width = window.innerWidth; | |
ch = canvas.height = window.innerHeight; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment