- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <!-- IF PEN IS PRIVATE -->
- <!-- <meta name="robots" content="noindex"> -->
- <!-- END -->
- <title>Canvas Fireworks · CodePen</title>
- <!--
- Copyright (c) 2012 Jack Rugile, http://codepen.io/jackrugile
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- -->
- <link rel="stylesheet" href="http://codepen.io/stylesheets/css/reset.css">
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
- body {
- background: #171717;
- color: #999;
- font: 100%/18px helvetica, arial, sans-serif;
- }
- a {
- color: #2fa1d6;
- font-weight: bold;
- text-decoration: none;
- }
- a:hover {
- color: #fff;
- }
- #canvas-container {
- background: #000 url(http://jackrugile.com/lab/fireworks-v2/images/bg.jpg);
- height: 400px;
- left: 50%;
- margin: -200px 0 0 -300px;
- position: absolute;
- top: 50%;
- width: 600px;
- z-index: 2;
- }
- canvas {
- cursor: crosshair;
- display: block;
- position: relative;
- z-index: 3;
- }
- canvas:active {
- cursor: crosshair;
- }
- #skyline {
- background: url(http://jackrugile.com/lab/fireworks-v2/images/skyline.png) repeat-x 50% 0;
- bottom: 0;
- height: 135px;
- left: 0;
- position: absolute;
- width: 100%;
- z-index: 1;
- }
- #mountains1 {
- background: url(http://jackrugile.com/lab/fireworks-v2/images/mountains1.png) repeat-x 40% 0;
- bottom: 0;
- height: 200px;
- left: 0;
- position: absolute;
- width: 100%;
- z-index: 1;
- }
- #mountains2 {
- background: url(http://jackrugile.com/lab/fireworks-v2/images/mountains2.png) repeat-x 30% 0;
- bottom: 0;
- height: 250px;
- left: 0;
- position: absolute;
- width: 100%;
- z-index: 1;
- }
- #gui {
- right: 0;
- position: fixed;
- top: 0;
- z-index: 3;
- }
- </style>
- <style>
- #codepen-footer, #codepen-footer * {
- -webkit-box-sizing: border-box !important;
- -moz-box-sizing: border-box !important;
- box-sizing: border-box !important;
- }
- #codepen-footer {
- display: block !important;
- position: fixed !important;
- bottom: 0 !important;
- left: 0 !important;
- width: 100% !important;
- padding: 0 10px !important;
- margin: 0 !important;
- height: 30px !important;
- line-height: 30px !important;
- font-size: 12px !important;
- color: #eeeeee !important;
- background-color: #505050 !important;
- text-align: left !important;
- background: -webkit-linear-gradient(top, #505050, #383838) !important;
- background: -moz-linear-gradient(top, #505050, #383838) !important;
- background: -ms-linear-gradient(top, #505050, #383838) !important;
- background: -o-linear-gradient(top, #505050, #383838) !important;
- border-top: 1px solid black !important;
- border-bottom: 1px solid black !important;
- border-radius: 0 !important;
- border-image: none !important;
- box-shadow: inset 0 1px 0 #6e6e6e, 0 2px 2px rgba(0, 0, 0, 0.4) !important;
- z-index: 300 !important;
- font-family: "Lucida Grande", "Lucida Sans Unicode", Tahoma, sans-serif !important;
- letter-spacing: 0 !important;
- word-spacing: normal !important;
- word-spacing: 0 !important;
- -webkit-transform: none !important;
- -moz-transform: none !important;
- -ms-transform: none !important;
- -o-transform: none !important;
- transform: none !important;
- }
- #codepen-footer a {
- color: #a7a7a7 !important;
- text-decoration: none !important;
- text-shadow: none !important;
- border: 0 !important;
- }
- #codepen-footer a:hover {
- color: white !important;
- }
- </style>
- <script>
- // Kill alerts, confirmations and prompts
- // window.alert = function(){}; we're going to allow alerts for now
- window.confirm = function(){};
- window.prompt = function(){};
- window.open = function(){};
- window.print = function(){};
- </script>
- <script src="http://codepen.io/javascripts/libs/prefixfree.min.js"></script>
- </head>
- <body>
- <div id="gui"></div>
- <div id="canvas-container">
- <div id="mountains2"></div>
- <div id="mountains1"></div>
- <div id="skyline"></div>
- </div>
- <script src="http://code.jquery.com/jquery-latest.js"></script>
- <script src="http://jackrugile.com/lab/fireworks-v2/js/dat.gui.min.js"></script>
- <script>
- (function() {
- var Fireworks = function(){
- /*=============================================================================*/
- /* Utility
- /*=============================================================================*/
- var self = this;
- var rand = function(rMi, rMa){return ~~((Math.random()*(rMa-rMi+1))+rMi);}
- var hitTest = function(x1, y1, w1, h1, x2, y2, w2, h2){return !(x1 + w1 < x2 || x2 + w2 < x1 || y1 + h1 < y2 || y2 + h2 < y1);};
- window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}();
- /*=============================================================================*/
- /* Initialize
- /*=============================================================================*/
- self.init = function(){
- self.canvas = document.createElement('canvas');
- self.canvasContainer = $('#canvas-container');
- var canvasContainerDisabled = document.getElementById('canvas-container');
- self.canvas.onselectstart = function() {
- return false;
- };
- self.canvas.width = self.cw = 600;
- self.canvas.height = self.ch = 400;
- self.particles = [];
- self.partCount = 30;
- self.fireworks = [];
- self.mx = self.cw/2;
- self.my = self.ch/2;
- self.currentHue = 170;
- self.partSpeed = 5;
- self.partSpeedVariance = 10;
- self.partWind = 50;
- self.partFriction = 5;
- self.partGravity = 1;
- self.hueMin = 150;
- self.hueMax = 200;
- self.fworkSpeed = 2;
- self.fworkAccel = 4;
- self.hueVariance = 30;
- self.flickerDensity = 20;
- self.showShockwave = false;
- self.showTarget = true;
- self.clearAlpha = 25;
- self.canvasContainer.append(self.canvas);
- self.ctx = self.canvas.getContext('2d');
- self.ctx.lineCap = 'round';
- self.ctx.lineJoin = 'round';
- self.lineWidth = 1;
- self.bindEvents();
- self.canvasLoop();
- self.canvas.onselectstart = function() {
- return false;
- };
- };
- /*=============================================================================*/
- /* Particle Constructor
- /*=============================================================================*/
- var Particle = function(x, y, hue){
- this.x = x;
- this.y = y;
- this.coordLast = [
- {x: x, y: y},
- {x: x, y: y},
- {x: x, y: y}
- ];
- this.angle = rand(0, 360);
- this.speed = rand(((self.partSpeed - self.partSpeedVariance) <= 0) ? 1 : self.partSpeed - self.partSpeedVariance, (self.partSpeed + self.partSpeedVariance));
- this.friction = 1 - self.partFriction/100;
- this.gravity = self.partGravity/2;
- this.hue = rand(hue-self.hueVariance, hue+self.hueVariance);
- this.brightness = rand(50, 80);
- this.alpha = rand(40,100)/100;
- this.decay = rand(10, 50)/1000;
- this.wind = (rand(0, self.partWind) - (self.partWind/2))/25;
- this.lineWidth = self.lineWidth;
- };
- Particle.prototype.update = function(index){
- var radians = this.angle * Math.PI / 180;
- var vx = Math.cos(radians) * this.speed;
- var vy = Math.sin(radians) * this.speed + this.gravity;
- this.speed *= this.friction;
- this.coordLast[2].x = this.coordLast[1].x;
- this.coordLast[2].y = this.coordLast[1].y;
- this.coordLast[1].x = this.coordLast[0].x;
- this.coordLast[1].y = this.coordLast[0].y;
- this.coordLast[0].x = this.x;
- this.coordLast[0].y = this.y;
- this.x += vx;
- this.y += vy;
- this.angle += this.wind;
- this.alpha -= this.decay;
- if(!hitTest(0,0,self.cw,self.ch,this.x-this.radius, this.y-this.radius, this.radius*2, this.radius*2) || this.alpha < .05){
- self.particles.splice(index, 1);
- }
- };
- Particle.prototype.draw = function(){
- var coordRand = (rand(1,3)-1);
- self.ctx.beginPath();
- self.ctx.moveTo(Math.round(this.coordLast[coordRand].x), Math.round(this.coordLast[coordRand].y));
- self.ctx.lineTo(Math.round(this.x), Math.round(this.y));
- self.ctx.closePath();
- self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+this.alpha+')';
- self.ctx.stroke();
- if(self.flickerDensity > 0){
- var inverseDensity = 50 - self.flickerDensity;
- if(rand(0, inverseDensity) === inverseDensity){
- self.ctx.beginPath();
- self.ctx.arc(Math.round(this.x), Math.round(this.y), rand(this.lineWidth,this.lineWidth+3)/2, 0, Math.PI*2, false)
- self.ctx.closePath();
- var randAlpha = rand(50,100)/100;
- self.ctx.fillStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+randAlpha+')';
- self.ctx.fill();
- }
- }
- };
- /*=============================================================================*/
- /* Create Particles
- /*=============================================================================*/
- self.createParticles = function(x,y, hue){
- var countdown = self.partCount;
- while(countdown--){
- self.particles.push(new Particle(x, y, hue));
- }
- };
- /*=============================================================================*/
- /* Update Particles
- /*=============================================================================*/
- self.updateParticles = function(){
- var i = self.particles.length;
- while(i--){
- var p = self.particles[i];
- p.update(i);
- };
- };
- /*=============================================================================*/
- /* Draw Particles
- /*=============================================================================*/
- self.drawParticles = function(){
- var i = self.particles.length;
- while(i--){
- var p = self.particles[i];
- p.draw();
- };
- };
- /*=============================================================================*/
- /* Firework Constructor
- /*=============================================================================*/
- var Firework = function(startX, startY, targetX, targetY){
- this.x = startX;
- this.y = startY;
- this.startX = startX;
- this.startY = startY;
- this.hitX = false;
- this.hitY = false;
- this.coordLast = [
- {x: startX, y: startY},
- {x: startX, y: startY},
- {x: startX, y: startY}
- ];
- this.targetX = targetX;
- this.targetY = targetY;
- this.speed = self.fworkSpeed;
- this.angle = Math.atan2(targetY - startY, targetX - startX);
- this.shockwaveAngle = Math.atan2(targetY - startY, targetX - startX)+(90*(Math.PI/180));
- this.acceleration = self.fworkAccel/100;
- this.hue = self.currentHue;
- this.brightness = rand(50, 80);
- this.alpha = rand(50,100)/100;
- this.lineWidth = self.lineWidth;
- this.targetRadius = 1;
- };
- Firework.prototype.update = function(index){
- self.ctx.lineWidth = this.lineWidth;
- vx = Math.cos(this.angle) * this.speed,
- vy = Math.sin(this.angle) * this.speed;
- this.speed *= 1 + this.acceleration;
- this.coordLast[2].x = this.coordLast[1].x;
- this.coordLast[2].y = this.coordLast[1].y;
- this.coordLast[1].x = this.coordLast[0].x;
- this.coordLast[1].y = this.coordLast[0].y;
- this.coordLast[0].x = this.x;
- this.coordLast[0].y = this.y;
- if(self.showTarget){
- if(this.targetRadius < 8){
- this.targetRadius += .25;
- } else {
- this.targetRadius = 1;
- }
- }
- if(this.startX >= this.targetX){
- if(this.x + vx <= this.targetX){
- this.x = this.targetX;
- this.hitX = true;
- } else {
- this.x += vx;
- }
- } else {
- if(this.x + vx >= this.targetX){
- this.x = this.targetX;
- this.hitX = true;
- } else {
- this.x += vx;
- }
- }
- if(this.startY >= this.targetY){
- if(this.y + vy <= this.targetY){
- this.y = this.targetY;
- this.hitY = true;
- } else {
- this.y += vy;
- }
- } else {
- if(this.y + vy >= this.targetY){
- this.y = this.targetY;
- this.hitY = true;
- } else {
- this.y += vy;
- }
- }
- if(this.hitX && this.hitY){
- var randExplosion = rand(0, 9);
- self.createParticles(this.targetX, this.targetY, this.hue);
- self.fireworks.splice(index, 1);
- }
- };
- Firework.prototype.draw = function(){
- self.ctx.lineWidth = this.lineWidth;
- var coordRand = (rand(1,3)-1);
- self.ctx.beginPath();
- self.ctx.moveTo(Math.round(this.coordLast[coordRand].x), Math.round(this.coordLast[coordRand].y));
- self.ctx.lineTo(Math.round(this.x), Math.round(this.y));
- self.ctx.closePath();
- self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+this.alpha+')';
- self.ctx.stroke();
- if(self.showTarget){
- self.ctx.save();
- self.ctx.beginPath();
- self.ctx.arc(Math.round(this.targetX), Math.round(this.targetY), this.targetRadius, 0, Math.PI*2, false)
- self.ctx.closePath();
- self.ctx.lineWidth = 1;
- self.ctx.stroke();
- self.ctx.restore();
- }
- if(self.showShockwave){
- self.ctx.save();
- self.ctx.translate(Math.round(this.x), Math.round(this.y));
- self.ctx.rotate(this.shockwaveAngle);
- self.ctx.beginPath();
- self.ctx.arc(0, 0, 1*(this.speed/5), 0, Math.PI, true);
- self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+rand(25, 60)/100+')';
- self.ctx.lineWidth = this.lineWidth;
- self.ctx.stroke();
- self.ctx.restore();
- }
- };
- /*=============================================================================*/
- /* Create Fireworks
- /*=============================================================================*/
- self.createFireworks = function(startX, startY, targetX, targetY){
- self.fireworks.push(new Firework(startX, startY, targetX, targetY));
- };
- /*=============================================================================*/
- /* Update Fireworks
- /*=============================================================================*/
- self.updateFireworks = function(){
- var i = self.fireworks.length;
- while(i--){
- var f = self.fireworks[i];
- f.update(i);
- };
- };
- /*=============================================================================*/
- /* Draw Fireworks
- /*=============================================================================*/
- self.drawFireworks = function(){
- var i = self.fireworks.length;
- while(i--){
- var f = self.fireworks[i];
- f.draw();
- };
- };
- /*=============================================================================*/
- /* Events
- /*=============================================================================*/
- self.bindEvents = function(){
- $(window).on('resize', function(){
- clearTimeout(self.timeout);
- self.timeout = setTimeout(function() {
- //self.canvas.width = self.cw = $(window).innerWidth();
- //self.canvas.height = self.ch = $(window).innerHeight();
- self.ctx.lineCap = 'round';
- self.ctx.lineJoin = 'round';
- }, 100);
- });
- $(self.canvas).on('mousedown', function(e){
- var randLaunch = rand(0, 5);
- self.mx = e.pageX - self.canvasContainer.offset().left;
- self.my = e.pageY - self.canvasContainer.offset().top;
- self.currentHue = rand(self.hueMin, self.hueMax);
- self.createFireworks(self.cw/2, self.ch, self.mx, self.my);
- $(self.canvas).on('mousemove.fireworks', function(e){
- var randLaunch = rand(0, 5);
- self.mx = e.pageX - self.canvasContainer.offset().left;
- self.my = e.pageY - self.canvasContainer.offset().top;
- self.currentHue = rand(self.hueMin, self.hueMax);
- self.createFireworks(self.cw/2, self.ch, self.mx, self.my);
- });
- });
- $(self.canvas).on('mouseup', function(e){
- $(self.canvas).off('mousemove.fireworks');
- });
- }
- /*=============================================================================*/
- /* Clear Canvas
- /*=============================================================================*/
- self.clear = function(){
- self.particles = [];
- self.fireworks = [];
- self.ctx.clearRect(0, 0, self.cw, self.ch);
- };
- /*=============================================================================*/
- /* Main Loop
- /*=============================================================================*/
- self.canvasLoop = function(){
- requestAnimFrame(self.canvasLoop, self.canvas);
- self.ctx.globalCompositeOperation = 'destination-out';
- self.ctx.fillStyle = 'rgba(0,0,0,'+self.clearAlpha/100+')';
- self.ctx.fillRect(0,0,self.cw,self.ch);
- self.ctx.globalCompositeOperation = 'lighter';
- self.updateFireworks();
- self.updateParticles();
- self.drawFireworks();
- self.drawParticles();
- };
- self.init();
- var initialLaunchCount = 10;
- while(initialLaunchCount--){
- setTimeout(function(){
- self.fireworks.push(new Firework(self.cw/2, self.ch, rand(50, self.cw-50), rand(50, self.ch/2)-50));
- }, initialLaunchCount*200);
- }
- }
- /*=============================================================================*/
- /* GUI
- /*=============================================================================*/
- var guiPresets = {
- "preset": "Default",
- "remembered": {
- "Default": {
- "0": {
- "fworkSpeed": 2,
- "fworkAccel": 4,
- "showShockwave": false,
- "showTarget": true,
- "partCount": 30,
- "partSpeed": 5,
- "partSpeedVariance": 10,
- "partWind": 50,
- "partFriction": 5,
- "partGravity": 1,
- "flickerDensity": 20,
- "hueMin": 150,
- "hueMax": 200,
- "hueVariance": 30,
- "lineWidth": 1,
- "clearAlpha": 25
- }
- },
- "Anti Gravity": {
- "0": {
- "fworkSpeed": 4,
- "fworkAccel": 10,
- "showShockwave": true,
- "showTarget": false,
- "partCount": 150,
- "partSpeed": 5,
- "partSpeedVariance": 10,
- "partWind": 10,
- "partFriction": 10,
- "partGravity": -10,
- "flickerDensity": 30,
- "hueMin": 0,
- "hueMax": 360,
- "hueVariance": 30,
- "lineWidth": 1,
- "clearAlpha": 50
- }
- },
- "Battle Field": {
- "0": {
- "fworkSpeed": 10,
- "fworkAccel": 20,
- "showShockwave": true,
- "showTarget": true,
- "partCount": 200,
- "partSpeed": 30,
- "partSpeedVariance": 5,
- "partWind": 0,
- "partFriction": 5,
- "partGravity": 0,
- "flickerDensity": 0,
- "hueMin": 20,
- "hueMax": 30,
- "hueVariance": 10,
- "lineWidth": 1,
- "clearAlpha": 40
- }
- },
- "Mega Blast": {
- "0": {
- "fworkSpeed": 3,
- "fworkAccel": 3,
- "showShockwave": true,
- "showTarget": true,
- "partCount": 500,
- "partSpeed": 50,
- "partSpeedVariance": 5,
- "partWind": 0,
- "partFriction": 0,
- "partGravity": 0,
- "flickerDensity": 0,
- "hueMin": 0,
- "hueMax": 360,
- "hueVariance": 30,
- "lineWidth": 20,
- "clearAlpha": 20
- }
- },
- "Nimble": {
- "0": {
- "fworkSpeed": 10,
- "fworkAccel": 50,
- "showShockwave": false,
- "showTarget": false,
- "partCount": 120,
- "partSpeed": 10,
- "partSpeedVariance": 10,
- "partWind": 100,
- "partFriction": 50,
- "partGravity": 0,
- "flickerDensity": 20,
- "hueMin": 0,
- "hueMax": 360,
- "hueVariance": 30,
- "lineWidth": 1,
- "clearAlpha": 80
- }
- },
- "Slow Launch": {
- "0": {
- "fworkSpeed": 2,
- "fworkAccel": 2,
- "showShockwave": false,
- "showTarget": false,
- "partCount": 200,
- "partSpeed": 10,
- "partSpeedVariance": 0,
- "partWind": 100,
- "partFriction": 0,
- "partGravity": 2,
- "flickerDensity": 50,
- "hueMin": 0,
- "hueMax": 360,
- "hueVariance": 20,
- "lineWidth": 4,
- "clearAlpha": 10
- }
- },
- "Perma Trail": {
- "0": {
- "fworkSpeed": 4,
- "fworkAccel": 10,
- "showShockwave": false,
- "showTarget": false,
- "partCount": 150,
- "partSpeed": 10,
- "partSpeedVariance": 10,
- "partWind": 100,
- "partFriction": 3,
- "partGravity": 0,
- "flickerDensity": 0,
- "hueMin": 0,
- "hueMax": 360,
- "hueVariance": 20,
- "lineWidth": 1,
- "clearAlpha": 0
- }
- }
- },
- "closed": true,
- "folders": {
- "Fireworks": {
- "preset": "Default",
- "closed": false,
- "folders": {}
- },
- "Particles": {
- "preset": "Default",
- "closed": true,
- "folders": {}
- },
- "Color": {
- "preset": "Default",
- "closed": true,
- "folders": {}
- },
- "Other": {
- "preset": "Default",
- "closed": true,
- "folders": {}
- }
- }
- };
- var fworks = new Fireworks();
- var gui = new dat.GUI({
- autoPlace: false,
- load: guiPresets,
- preset: 'Default'
- });
- var customContainer = document.getElementById('gui');
- customContainer.appendChild(gui.domElement);
- var guiFireworks = gui.addFolder('Fireworks');
- guiFireworks.add(fworks, 'fworkSpeed').min(1).max(10).step(1);
- guiFireworks.add(fworks, 'fworkAccel').min(0).max(50).step(1);
- guiFireworks.add(fworks, 'showShockwave');
- guiFireworks.add(fworks, 'showTarget');
- var guiParticles = gui.addFolder('Particles');
- guiParticles.add(fworks, 'partCount').min(0).max(500).step(1);
- guiParticles.add(fworks, 'partSpeed').min(1).max(100).step(1);
- guiParticles.add(fworks, 'partSpeedVariance').min(0).max(50).step(1);
- guiParticles.add(fworks, 'partWind').min(0).max(100).step(1);
- guiParticles.add(fworks, 'partFriction').min(0).max(50).step(1);
- guiParticles.add(fworks, 'partGravity').min(-20).max(20).step(1);
- guiParticles.add(fworks, 'flickerDensity').min(0).max(50).step(1);
- var guiColor = gui.addFolder('Color');
- guiColor.add(fworks, 'hueMin').min(0).max(360).step(1);
- guiColor.add(fworks, 'hueMax').min(0).max(360).step(1);
- guiColor.add(fworks, 'hueVariance').min(0).max(180).step(1);
- var guiOther = gui.addFolder('Other');
- guiOther.add(fworks, 'lineWidth').min(1).max(20).step(1);
- guiOther.add(fworks, 'clearAlpha').min(0).max(100).step(1);
- guiOther.add(fworks, 'clear').name('Clear');
- gui.remember(fworks);
- })();
- </script>
- <div id="codepen-footer">
- <a style="color: #f73535 !important;" href="https://codepen.wufoo.com/forms/m7x3r3/def/field14=" target="_blank">Report Abuse</a>
-
- <a href="/jackrugile/pen/acAgx" target="_blank">Edit this Pen</a>
- </div>
- </body>
- </html>