firework-garden.js

total 0
used 0
limit 0
/* title: Firework Garden categories: animation particles files: ../point_src/core/head.js ../point_src/pointpen.js ../point_src/pointdraw.js ../point_src/math.js ../point_src/compass.js ../point_src/point.js ../point_src/stage.js */ class MainStage extends Stage { canvas = 'playspace' mounted() { this.rockets = [] this.sparks = [] this.wheels = [] this.frame = 0 this.palette = ['#ff557f', '#ffca52', '#60edff', '#a68cff', '#7bffb2', '#ff8b48', '#f9f6ff'] } launch(w, h) { const p = new Point(w * (.12 + Math.random() * .76), h + 8, 3) p.vx = (Math.random() - .5) * 1.3 p.vy = -(5.5 + Math.random() * 6.8) p.fuse = 32 + Math.random() * 58 p.hue = this.palette[Math.floor(Math.random() * this.palette.length)] const choice = Math.random() p.kind = choice < .14 ? 'wheel' : choice < .29 ? 'globe' : choice < .49 ? 'willow' : choice < .68 ? 'peony' : 'normal' this.rockets.push(p) } makeWheel(rocket) { this.wheels.push({center: new Point(rocket.x, rocket.y, 1), angle: Math.random() * Math.PI * 2, spin: (Math.random() < .5 ? -1 : 1) * .2, age: 0, hue: rocket.hue}) } willow(rocket) { for (let i = 0; i < 90; i++) { const a = Math.PI * 2 * i / 90 const sp = 1.6 + Math.random() * 1.8 const p = new Point(rocket.x, rocket.y, .65) p.vx = Math.cos(a) * sp p.vy = Math.sin(a) * sp p.life = 160 + Math.random() * 35 p.hue = Math.random() < .2 ? '#fff2aa' : '#e9ad4a' p.willow = true p.trail = [] this.sparks.push(p) } } peony(rocket) { // Fibonacci sphere: evenly spaced directions on a 3D shell. // Project each direction into the screen, scaling with depth. const count = 220 const goldenAngle = Math.PI * (3 - Math.sqrt(5)) const spin = Math.random() * Math.PI * 2 const tilt = (Math.random() - .5) * .8 for (let i = 0; i < count; i++) { const z = 1 - 2 * (i + .5) / count const ring = Math.sqrt(1 - z * z) const angle = i * goldenAngle + spin const xx = ring * Math.cos(angle) const yy = ring * Math.sin(angle) const zz = z * Math.cos(tilt) + yy * Math.sin(tilt) const yz = yy * Math.cos(tilt) - z * Math.sin(tilt) const perspective = 2.6 / (2.6 - zz * .7) const p = new Point(rocket.x, rocket.y, .55 + .65 * perspective) p.vx = xx * 3.25 * perspective p.vy = yz * 3.25 * perspective p.peony = true p.depth = zz p.life = 105 p.maxLife = 105 p.hue = (i % 9 === 0) ? '#ffffff' : rocket.hue this.sparks.push(p) } } burst(rocket) { if (rocket.kind === 'willow') { this.willow(rocket); return } if (rocket.kind === 'peony') { this.peony(rocket); return } if (rocket.kind === 'wheel') { this.makeWheel(rocket); return } const globe = rocket.kind === 'globe' const n = globe ? 120 : 75 + Math.floor(Math.random() * 55) for (let i = 0; i < n; i++) { const a = Math.PI * 2 * i / n + (globe ? 0 : Math.random() * .09) const speed = globe ? 2.7 : (1.1 + Math.random() * 3.8) * (i % 7 === 0 ? 1.35 : 1) const p = new Point(rocket.x, rocket.y, .6 + Math.random() * 1.15) p.vx = Math.cos(a) * speed p.vy = Math.sin(a) * speed p.life = globe ? 95 : 55 + Math.random() * 55 p.globe = globe p.maxLife = p.life p.hue = globe ? rocket.hue : (Math.random() < .19 ? '#ffffff' : rocket.hue) this.sparks.push(p) } } draw(ctx) { const w = ctx.canvas.width, h = ctx.canvas.height this.clear(ctx) ctx.fillStyle = '#08091a' ctx.fillRect(0, 0, w, h) this.frame++ if (this.frame % 21 === 1) this.launch(w, h) ctx.save() ctx.globalCompositeOperation = 'lighter' for (let i = this.rockets.length - 1; i >= 0; i--) { const p = this.rockets[i] p.x += p.vx; p.y += p.vy; p.vy += .075; p.fuse-- ctx.strokeStyle = '#e5b9ff' ctx.lineWidth = 2 ctx.beginPath(); ctx.moveTo(p.x, p.y + 14); ctx.lineTo(p.x, p.y); ctx.stroke() p.pen.fill(ctx, '#fff5db') if (p.fuse <= 0 || p.vy >= -.7) { this.burst(p) this.rockets.splice(i, 1) } } for (let i = this.wheels.length - 1; i >= 0; i--) { const wheel = this.wheels[i] wheel.age++ wheel.angle += wheel.spin for (let arm = 0; arm < 3; arm++) { const a = wheel.angle + arm * Math.PI * 2 / 3 const d = 10 + Math.min(wheel.age, 25) * .6 const p = new Point(wheel.center.x + Math.cos(a) * d, wheel.center.y + Math.sin(a) * d, .7) p.vx = Math.cos(a + Math.PI / 2) * 2.5 p.vy = Math.sin(a + Math.PI / 2) * 2.5 p.life = 26 + Math.random() * 15 p.hue = Math.random() < .2 ? '#ffffff' : wheel.hue this.sparks.push(p) } if (wheel.age > 65) this.wheels.splice(i, 1) } for (let i = this.sparks.length - 1; i >= 0; i--) { const p = this.sparks[i] if (p.willow) { if (this.frame % 2 === 0) p.trail.push([p.x, p.y]) if (p.trail.length > 28) p.trail.shift() ctx.globalAlpha = Math.min(.75, p.life / 75) ctx.strokeStyle = p.hue ctx.lineWidth = .7 ctx.beginPath() p.trail.forEach((xy, k) => { if (k === 0) ctx.moveTo(xy[0], xy[1]) else ctx.lineTo(xy[0], xy[1]) }) ctx.stroke() } p.x += p.vx; p.y += p.vy p.vx *= p.willow ? .993 : p.peony ? .994 : p.globe ? .99 : .985 p.vy = p.vy * (p.willow ? .995 : p.peony ? .994 : p.globe ? .99 : .985) + (p.willow ? .035 : p.peony ? .002 : p.globe ? .006 : .035) p.life-- if (p.life <= 0) { this.sparks.splice(i, 1); continue } ctx.globalAlpha = Math.min(1, p.life / (p.willow ? 80 : p.peony ? 40 : p.globe ? 40 : 25)) * (p.peony ? .68 + .32 * (p.depth + 1) / 2 : 1) p.pen.fill(ctx, p.hue) } ctx.restore() } } stage = MainStage.go()
Run
Meta Data
title Firework Garden
imports ()
files ('../point_src/core/head.js', '../point_src/pointpen.js', '../point_src/pointdraw.js', '../point_src/math.js', '../point_src/compass.js', '../point_src/point.js', '../point_src/stage.js')
unused_keys ()
unknown_keys ('categories',)
categories ['animation', 'particles']
filepath_exists True
path firework-garden.js
filepath firework-garden.js
clean_files ('../point_src/core/head.js', '../point_src/pointpen.js', '../point_src/pointdraw.js', '../point_src/math.js', '../point_src/compass.js', '../point_src/relative-xy.js', '../point_src/pointcast.js', '../point_src/point.js', '../point_src/stage-hooks.js', '../point_src/functions/resolve.js', '../point_src/stage.js')
markdown {'html': '', 'content': 'title: Firework Garden\ncategories: animation\n particles\nfiles:\n ../point_src/core/head.js\n ../point_src/pointpen.js\n ../point_src/pointdraw.js\n ../point_src/math.js\n ../point_src/compass.js\n ../point_src/point.js\n ../point_src/stage.js'}