{"version":3,"names":[],"mappings":"","sources":["main.js"],"sourcesContent":["'use strict';\n\nclass Vector {\n constructor(dx, dy) {\n this.x = dx;\n this.y = dy;\n this.mag = undefined;\n this.head = undefined;\n }\n\n get copy() {\n return new Vector(this.x, this.y);\n }\n\n add(v) {\n this.x += v.x;\n this.y += v.y;\n this.mag = undefined;\n this.head = undefined;\n return this;\n }\n\n subtract(v) {\n this.x -= v.x;\n this.y -= v.y;\n this.mag = undefined;\n this.head = undefined;\n return this;\n }\n\n scale(scalar) {\n this.x *= scalar;\n this.y *= scalar;\n this.mag = undefined;\n this.head = undefined;\n return this;\n }\n\n dot(v) {\n return this.x * v.x + this.y * v.y;\n }\n\n get magnitude() {\n return this.mag !== undefined ? this.mag : (this.mag = Math.sqrt(this.dot(this)));\n }\n\n normalize() {\n return this.scale(1 / this.magnitude);\n }\n\n get heading() {\n return this.head !== undefined ? this.head : (this.head = Math.atan2(this.y, this.x));\n }\n\n distance(v) {\n let dx = v.x - this.x;\n let dy = v.y - this.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n limit(lim) {\n if (this.magnitude > lim) {\n this.normalize().scale(lim);\n }\n return this;\n }\n\n static vectorFromSpeedAndAngle(speed, angle) {\n let angleRadians = angle * Math.PI / 180.0;\n let x = speed * Math.cos(angleRadians);\n let y = speed * Math.sin(angleRadians);\n\n return new Vector(x, y);\n }\n}\n\nconst canvas = document.getElementById('fireworks');\nconst ctx = canvas.getContext('2d');\n\nconst GRAVITY = new Vector(0, 0.1);\nconst MAX_VEL = 8;\n\nlet cWidth = canvas.scrollWidth;\nlet cHeight = canvas.scrollHeight;\n\nlet gradient = ctx.createLinearGradient(cWidth / 2, 0, cWidth / 2, cHeight);\n\nlet particles = [];\nlet mortars = [];\n\nfunction rnd(min = 0, max = 100) {\n return (Math.random() * (max - min)) + min;\n}\n\nfunction rndi(min = 0, max = 100) {\n return Math.floor( (Math.random() * (max - min)) + min );\n}\n\nfunction clamp(value, min = 0, max = 1.0) {\n return Math.max(min, Math.min(max, value));\n}\n\nfunction updateCanvasSize() {\n cWidth = canvas.scrollWidth;\n cHeight = canvas.scrollHeight;\n\n ctx.canvas.width = cWidth;\n ctx.canvas.height = cHeight;\n\n gradient = ctx.createLinearGradient(cWidth / 2, 0, cWidth / 2, cHeight);\n gradient.addColorStop(0, '#000000');\n gradient.addColorStop(0.45, '#0c2753');\n gradient.addColorStop(0.65, '#003b76');\n gradient.addColorStop(1.0, '#007b97');\n}\n\nfunction outOfBounds(v) {\n return (v.x < 0 || v.x > cWidth || v.y < 0 || v.y > cHeight);\n}\n\nfunction createParticle(centX, centY, velVect, fillColor, lifeSpan) {\n return {\n pos: new Vector(centX, centY),\n vel: velVect || new Vector(rnd(-MAX_VEL, MAX_VEL), rnd(MAX_VEL, -MAX_VEL)),\n fill: fillColor || `#${ (rndi(100, 255)).toString(16) }${ (rndi(100, 255)).toString(16) }${ (rndi(100, 255)).toString(16) }`,\n life: lifeSpan || 80,\n origLife: lifeSpan || 80\n };\n}\n\nfunction createMortar(cx, cy) {\n let target = new Vector(cx || rndi(cWidth / 3, cWidth / 3 * 2), cy || rndi(cHeight / 4, cHeight / 2));\n let vel = target.subtract(new Vector(cWidth / 2, cHeight));\n vel.normalize().scale(rnd(8, 10));\n return createParticle(cWidth / 2, cHeight, vel, '#00a7fe');\n}\n\nfunction createBurst(qty = 30, cx = cWidth / 2, cy = cHeight / 2) {\n for(let i = 0; i < qty; i++) {\n particles.push(createParticle(cx, cy));\n }\n}\n\nfunction createSpray(qty = 30, cx = cWidth / 2, cy = cHeight / 2) {\n let angle = rnd(250, 290);\n for (let i = 0; i < qty; i++) {\n let a = angle;\n let speed = 1;\n let variance = rnd(0, 100);\n if (variance < 40) {\n a += rnd(-15, 15);\n speed += rnd(1, 5);\n }\n else if (variance < 60) {\n a += rnd(-25, -15);\n speed += rnd(1, 4);\n }\n else if (variance < 80) {\n a += rnd(15, 25);\n speed += rnd(1, 4);\n }\n else if (variance < 90) {\n a += rnd(-35, -25);\n speed += rnd(1, 2);\n }\n else {\n a += rnd(25, 35);\n speed += rnd(1, 2);\n }\n let v = Vector.vectorFromSpeedAndAngle(speed, a);\n particles.push(createParticle(cx, cy, v));\n }\n}\n\nfunction clearCanvas() {\n ctx.save();\n ctx.globalAlpha = 0.33;\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, cWidth, cHeight);\n ctx.restore();\n}\n\nfunction moveAndPaintParticles() {\n if (particles.length === 0) {\n return;\n }\n\n particles.forEach((p, i) => {\n if (p.life <= 0 || outOfBounds(p.pos)) {\n particles.splice(i, 1);\n return;\n }\n\n p.vel.add(GRAVITY);\n p.vel.limit(MAX_VEL);\n p.pos.add(p.vel);\n\n let alpha = clamp(p.life / p.origLife);\n ctx.save();\n ctx.fillStyle = p.fill;\n ctx.globalAlpha = alpha;\n // ctx.fillRect(p.pos.x, p.pos.y, 1, 1);\n ctx.beginPath();\n ctx.arc(p.pos.x, p.pos.y, 2, 0, Math.PI * 2);\n ctx.fill();\n ctx.closePath();\n ctx.restore();\n\n p.life--;\n });\n}\n\nfunction moveAndPaintMortars() {\n if (mortars.length === 0) {\n return;\n }\n\n mortars.forEach((p, i) => {\n\n //If it reaches the point that it's not travelling upwards anymore\n if (p.vel.y > 0) {\n let type = rndi(); //0-100 range\n // 10% chance double burst\n if (type < 10) {\n createBurst(rndi(30, 60), p.pos.x + rndi(-10, 10), p.pos.y + rndi(-10, 10));\n createBurst(rndi(30, 60), p.pos.x + rndi(-10, 10), p.pos.y + rndi(-10, 10));\n }\n else if (type < 20) { // 10% burst and spray\n createBurst(rndi(30, 60), p.pos.x, p.pos.y);\n createSpray(rndi(30, 60), p.pos.x + rndi(-10, 10), p.pos.y + rndi(-10, 10));\n }\n else if (type < 30) { // 10% double spray\n createSpray(rndi(30, 60), p.pos.x + rndi(-10, 10), p.pos.y + rndi(-10, 10));\n createSpray(rndi(30, 60), p.pos.x + rndi(-10, 10), p.pos.y + rndi(-10, 10));\n }\n else if (type < 50) { // 20% single spray, more particles\n createSpray(rndi(50, 80), p.pos.x, p.pos.y);\n }\n else { // 50% regular burst\n createBurst(rndi(30, 60), p.pos.x, p.pos.y);\n }\n\n mortars.splice(i, 1);\n return;\n }\n\n p.vel.add(GRAVITY);\n // p.vel.limit(MAX_VEL);\n p.pos.add(p.vel);\n\n ctx.fillStyle = p.fill;\n ctx.beginPath();\n ctx.arc(p.pos.x, p.pos.y, 3, 0, Math.PI * 2);\n ctx.fill();\n ctx.closePath();\n });\n}\n\nfunction render() {\n clearCanvas();\n moveAndPaintMortars();\n moveAndPaintParticles();\n requestAnimationFrame(render);\n}\n\nfunction launchFirework() {\n if(mortars.length < 4){\n mortars.push(createMortar());\n }\n setTimeout(launchFirework, rndi(200, 800));\n}\n\nupdateCanvasSize();\nwindow.addEventListener('resize', updateCanvasSize);\ncanvas.onclick = (evt) => {\n mortars.push(createMortar(evt.clientX, evt.clientY));\n};\nrender();\nlaunchFirework();\n"],"file":"main.js"}