@mar7th/firework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ (function(c,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(c=typeof globalThis<"u"?globalThis:c||self,u(c.SparkFireworks={}))})(this,function(c){"use strict";var re=Object.defineProperty;var se=(c,u,f)=>u in c?re(c,u,{enumerable:!0,configurable:!0,writable:!0,value:f}):c[u]=f;var L=(c,u,f)=>se(c,typeof u!="symbol"?u+"":u,f);const f={particleCount:180,particleMinSize:2.5,particleMaxSize:7,sizeEndMultiplier:.35,gravity:.18,damping:.982,initialSpeedMin:2.8,initialSpeedMax:8.2,particleLife:94,rotationSpeed:.025,colorShiftRate:.035,colorStart:"#ffbf4d",colorEnd:"#ff5a3c",useRandomColors:!1,textureType:"circle",glowIntensity:.9,explosionShape:"sphere",enableSecondaryBurst:!1,secondaryBurstRatio:.2,enableTrails:!0,showLaunchTrail:!0,windEnabled:!1,windX:0,windY:0,flicker:!0,fadeTrail:.18,autoLaunch:!1,autoInterval:900,autoLaunchIntervalSeconds:.9,backgroundType:"starfield",backgroundColor:"#170d17",backgroundColor2:"#3a1a24",backgroundImageMode:"cover"},B=[{title:"样式",controls:[{key:"colorStart",label:"起始颜色",type:"color"},{key:"colorEnd",label:"结束颜色",type:"color"},{key:"useRandomColors",label:"随机颜色",type:"boolean"},{key:"textureType",label:"粒子形状",type:"select",options:[["circle","圆形"],["square","方形"],["star","星形"],["heart","心形"]]},{key:"glowIntensity",label:"辉光强度",type:"range",min:0,max:1.5,step:.05}]},{title:"特效",controls:[{key:"explosionShape",label:"爆炸形状",type:"select",options:[["sphere","球形"],["ring","环形"],["starburst","星形"],["flower","花朵"],["willow","柳絮"],["scatter","随机散落"],["heart","爱心"]]},{key:"enableSecondaryBurst",label:"二次爆炸",type:"boolean",hint:"二次爆炸对性能影响较大,谨慎开启。"},{key:"secondaryBurstRatio",label:"二次比例",type:"range",min:0,max:1,step:.05,hint:"这里只控制二次爆炸比例;自定义二次爆炸效果需要使用高级模式自定义脚本。"},{key:"enableTrails",label:"粒子拖尾",type:"boolean",hint:"拖尾会影响性能,酌情开启。"},{key:"showLaunchTrail",label:"升空轨迹",type:"boolean"},{key:"windEnabled",label:"风场",type:"boolean"},{key:"windX",label:"水平风",type:"range",min:-.35,max:.35,step:.01},{key:"windY",label:"垂直风",type:"range",min:-.35,max:.35,step:.01},{key:"flicker",label:"闪烁",type:"boolean"},{key:"fadeTrail",label:"拖尾长度",type:"range",min:.04,max:.55,step:.01}]},{title:"粒子",controls:[{key:"particleCount",label:"粒子数量",type:"range",min:10,max:1e3,step:10},{key:"initialSpeedMin",label:"最小速度",type:"range",min:0,max:20,step:.1},{key:"initialSpeedMax",label:"最大速度",type:"range",min:0,max:20,step:.1},{key:"damping",label:"速度衰减",type:"range",min:.95,max:1,step:.001},{key:"gravity",label:"重力影响",type:"range",min:-1,max:2,step:.01},{key:"particleLife",label:"生命周期",type:"range",min:30,max:180,step:1},{key:"particleMinSize",label:"最小大小",type:"range",min:2,max:15,step:.5},{key:"particleMaxSize",label:"最大大小",type:"range",min:2,max:15,step:.5},{key:"sizeEndMultiplier",label:"结束比例",type:"range",min:0,max:2,step:.05},{key:"rotationSpeed",label:"旋转速度",type:"range",min:0,max:.105,step:.005},{key:"colorShiftRate",label:"变色速率",type:"range",min:0,max:.1,step:.005},{key:"autoLaunchIntervalSeconds",label:"自动间隔(秒)",type:"range",min:.25,max:3,step:.05}]},{title:"背景",controls:[{key:"backgroundType",label:"背景样式",type:"select",options:[["solid","纯色"],["linear","线性渐变"],["radial","径向渐变"],["starfield","星空"],["image","图片"]]},{key:"backgroundColor",label:"背景色 A",type:"color"},{key:"backgroundColor2",label:"背景色 B",type:"color"},{key:"backgroundImageMode",label:"图片填充",type:"select",options:[["cover","覆盖"],["contain","完整"],["tile","平铺"]]},{key:"backgroundImage",label:"上传图片",type:"file"}]}],w={"classic-gold":{name:"经典礼花",config:{colorStart:"#ffd166",colorEnd:"#ff3d2e",explosionShape:"sphere",particleCount:220,gravity:.22,damping:.981,glowIntensity:.9,enableSecondaryBurst:!1,textureType:"circle",fadeTrail:.18}},aurora:{name:"梦幻极光",config:{colorStart:"#4cc9f0",colorEnd:"#80ffdb",explosionShape:"ring",particleCount:260,gravity:.03,damping:.992,glowIntensity:1.1,fadeTrail:.08,textureType:"circle"}},fountain:{name:"星光喷泉",config:{colorStart:"#f7f3a1",colorEnd:"#fca311",explosionShape:"flower",particleCount:420,particleMinSize:2,particleMaxSize:4.5,initialSpeedMin:4,initialSpeedMax:11,gravity:.32,damping:.976,textureType:"star"}},ghost:{name:"幽灵鬼火",config:{colorStart:"#7bdff2",colorEnd:"#b2f7ef",explosionShape:"scatter",particleCount:160,initialSpeedMin:.8,initialSpeedMax:4.6,gravity:-.01,damping:.989,enableSecondaryBurst:!0,glowIntensity:1.2,fadeTrail:.12}},meteor:{name:"节日流星",config:{colorStart:"#ffafcc",colorEnd:"#ffffff",explosionShape:"willow",particleCount:300,initialSpeedMin:7,initialSpeedMax:13,damping:.965,gravity:.16,windEnabled:!0,windX:.12,textureType:"square"}},heart:{name:"爱心爆发",config:{colorStart:"#ff4d6d",colorEnd:"#ffd6e0",explosionShape:"heart",particleCount:260,gravity:.08,damping:.986,textureType:"heart",glowIntensity:1}}};function p(n){const e={...f};if(!n||typeof n!="object")return e;const t=B.flatMap(i=>i.controls);for(const i of t)if(i.key in n){if(i.type==="range"){const a=Number(n[i.key]);Number.isFinite(a)&&(e[i.key]=Math.min(i.max,Math.max(i.min,a)))}else if(i.type==="boolean")e[i.key]=!!n[i.key];else if(i.type==="select")i.options.map(([r])=>r).includes(n[i.key])&&(e[i.key]=n[i.key]);else if(i.type==="color"){const a=String(n[i.key]);/^#[0-9a-f]{6}$/i.test(a)&&(e[i.key]=a)}}if("autoLaunch"in n&&(e.autoLaunch=!!n.autoLaunch),"autoInterval"in n){const i=Number(n.autoInterval);Number.isFinite(i)&&(e.autoInterval=I(i,250,3e3),e.autoLaunchIntervalSeconds=A(e.autoInterval))}if("autoLaunchIntervalSeconds"in n){const i=Number(n.autoLaunchIntervalSeconds);Number.isFinite(i)&&(e.autoLaunchIntervalSeconds=I(i,.25,3),e.autoInterval=z(e.autoLaunchIntervalSeconds))}return e}function I(n,e,t){return Math.min(t,Math.max(e,n))}function z(n){return Math.round(n*1e3)}function A(n){return Math.round(n/1e3*100)/100}class N{constructor(){L(this,"listeners",new Map)}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}off(e,t){var i;(i=this.listeners.get(e))==null||i.delete(t)}emit(e,t){var i;(i=this.listeners.get(e))==null||i.forEach(a=>a(t))}}function C(n,e,t){return Math.min(t,Math.max(e,n))}function M(n,e,t){return n+(e-n)*t}function h(n,e){return Math.random()*(e-n)+n}function y(n,e){return Math.floor(h(n,e+1))}class V{constructor(){this.image=null,this.imageUrl="",this.stars=[]}resize(e,t){const i=Math.round(Math.min(260,Math.max(90,e*t/9e3)));this.stars=Array.from({length:i},()=>({x:Math.random()*e,y:Math.random()*t,r:Math.random()*1.4+.25,a:Math.random()*.65+.18}))}setImage(e,t,i){if(!e)return;if(e.size>2*1024*1024){i==null||i("背景图片不能超过 2MB");return}this.imageUrl&&URL.revokeObjectURL(this.imageUrl),this.imageUrl=URL.createObjectURL(e);const a=new Image;a.onload=()=>{this.image=a,t==null||t()},a.onerror=()=>{this.image=null,i==null||i("背景图片读取失败")},a.src=this.imageUrl}draw(e,t,i,a){if(a.backgroundType==="image"&&this.image){this.drawImage(e,t,i,a.backgroundImageMode);return}if(a.backgroundType==="linear"){const r=e.createLinearGradient(0,0,t,i);r.addColorStop(0,a.backgroundColor),r.addColorStop(1,a.backgroundColor2),e.fillStyle=r,e.fillRect(0,0,t,i);return}if(a.backgroundType==="radial"||a.backgroundType==="starfield"){const r=e.createRadialGradient(t*.5,i*.35,0,t*.5,i*.5,Math.max(t,i));r.addColorStop(0,a.backgroundColor2),r.addColorStop(1,a.backgroundColor),e.fillStyle=r,e.fillRect(0,0,t,i),a.backgroundType==="starfield"&&this.drawStars(e);return}e.fillStyle=a.backgroundColor,e.fillRect(0,0,t,i)}drawStars(e){e.save();for(const t of this.stars)e.fillStyle=`rgba(255, 255, 255, ${t.a})`,e.beginPath(),e.arc(t.x,t.y,t.r,0,Math.PI*2),e.fill();e.restore()}drawImage(e,t,i,a){if(a==="tile"){const m=e.createPattern(this.image,"repeat");e.fillStyle=m,e.fillRect(0,0,t,i);return}const r=a==="contain"?Math.min(t/this.image.width,i/this.image.height):Math.max(t/this.image.width,i/this.image.height),s=this.image.width*r,o=this.image.height*r,l=(t-s)/2,d=(i-o)/2;e.fillStyle="#050711",e.fillRect(0,0,t,i),e.drawImage(this.image,l,d,s,o)}}function P(n){const e=n.replace("#",""),t=Number.parseInt(e,16);return{r:t>>16&255,g:t>>8&255,b:t&255}}function S({r:n,g:e,b:t},i=1){return`rgba(${Math.round(n)}, ${Math.round(e)}, ${Math.round(t)}, ${C(i,0,1)})`}function j(n,e,t){const i=typeof n=="string"?P(n):n,a=typeof e=="string"?P(e):e;return{r:M(i.r,a.r,t),g:M(i.g,a.g,t),b:M(i.b,a.b,t)}}function v(){const n=y(0,360);return X(n,y(72,96),y(54,68))}function X(n,e,t){e/=100,t/=100;const i=(1-Math.abs(2*t-1))*e,a=i*(1-Math.abs(n/60%2-1)),r=t-i/2;let s=0,o=0,l=0;return n<60?[s,o,l]=[i,a,0]:n<120?[s,o,l]=[a,i,0]:n<180?[s,o,l]=[0,i,a]:n<240?[s,o,l]=[0,a,i]:n<300?[s,o,l]=[a,0,i]:[s,o,l]=[i,0,a],`#${x((s+r)*255)}${x((o+r)*255)}${x((l+r)*255)}`}function x(n){return Math.round(n).toString(16).padStart(2,"0")}class ${constructor(e){this.getConfig=e}createExplosion(e,t,i={}){const a={...this.getConfig(),...i},r=Math.min(1e3,Math.max(10,Math.round(a.particleCount))),s=[],o=a.useRandomColors?v():a.colorStart,l=a.useRandomColors?v():a.colorEnd,d=a.enableSecondaryBurst?Math.round(r*O(a.secondaryBurstRatio)):0,m=U(r,d);for(let g=0;g<r;g+=1){const b=q(a.explosionShape,g,r),E=b.speedMultiplier*h(a.initialSpeedMin,a.initialSpeedMax),Z=a.explosionShape==="heart"?b.offsetX:0,ee=a.explosionShape==="heart"?b.offsetY:0,te=a.useRandomColors&&g%4===0?v():o,ie=a.useRandomColors&&g%5===0?v():l,ae=h(a.particleMinSize,a.particleMaxSize),ne=h(a.particleLife*.78,a.particleLife*1.18);s.push({x:e+Z,y:t+ee,vx:b.x*E,vy:b.y*E,size:ae,life:ne,startColor:te,endColor:ie,textureType:a.textureType,rotationVelocity:h(-a.rotationSpeed,a.rotationSpeed),secondaryEligible:m.has(g)})}return s}createSubExplosion(e,t,i=null){const a=(i==null?void 0:i.config)||this.getConfig(),r=this.createConfigFromParticle(i,a),s=(i==null?void 0:i.secondaryBurstConfig)||{},o={...r,...s,enableSecondaryBurst:!1},l="particleCount"in s?o.particleCount:Math.max(12,Math.round(r.particleCount*.16)),d="particleLife"in s?o.particleLife:Math.max(28,r.particleLife*.48),m="initialSpeedMin"in s?o.initialSpeedMin:Math.max(.4,r.initialSpeedMin*.38),g="initialSpeedMax"in s?o.initialSpeedMax:Math.max(1,r.initialSpeedMax*.56);return this.createExplosion(e,t,{...o,particleCount:l,particleLife:d,initialSpeedMin:m,initialSpeedMax:g,enableSecondaryBurst:!1})}createConfigFromParticle(e,t){return e?{...t,colorStart:e.startColor||t.colorStart,colorEnd:e.endColor||t.colorEnd,useRandomColors:!1,textureType:e.textureType||t.textureType,particleMinSize:e.size,particleMaxSize:e.size,particleLife:e.maxLife,enableSecondaryBurst:!1}:t}createLaunch(e,t,i,a){const r=this.getConfig();return new Y(e,t,i,a,r)}}function O(n){return Number.isFinite(Number(n))?Math.min(1,Math.max(0,Number(n))):0}function U(n,e){const t=new Set;for(;t.size<e;)t.add(y(0,n-1));return t}class Y{constructor(e,t,i,a,r){this.x=e,this.y=t,this.targetX=i,this.targetY=a,this.color=r.colorStart,this.life=1,this.path=[];const s=i-e,o=a-t,l=Math.hypot(s,o)||1,d=Math.min(18,Math.max(9,l/28));this.vx=s/l*d,this.vy=o/l*d}update(e){return this.path.push({x:this.x,y:this.y}),this.path.length>18&&this.path.shift(),this.x+=this.vx*e,this.y+=this.vy*e,this.vy+=.03*e,Math.hypot(this.targetX-this.x,this.targetY-this.y)<14||this.vy>1.5}draw(e,t){e.save(),e.strokeStyle="rgba(255, 230, 170, 0.35)",e.fillStyle=this.color,e.shadowColor=this.color,e.shadowBlur=16,t&&this.path.length>1&&(e.beginPath(),this.path.forEach((i,a)=>{a===0?e.moveTo(i.x,i.y):e.lineTo(i.x,i.y)}),e.stroke()),e.beginPath(),e.arc(this.x,this.y,3,0,Math.PI*2),e.fill(),e.restore()}}function q(n,e,t){if(n==="ring"){const a=e/t*Math.PI*2;return{x:Math.cos(a),y:Math.sin(a),speedMultiplier:1}}if(n==="starburst"){const s=y(0,4)/5*Math.PI*2-Math.PI/2+h(-.16,.16);return{x:Math.cos(s),y:Math.sin(s),speedMultiplier:e%3===0?1.25:.62}}if(n==="flower"){const a=e/t*Math.PI*2,r=.58+Math.abs(Math.sin(a*6))*.72;return{x:Math.cos(a),y:Math.sin(a),speedMultiplier:r}}if(n==="willow"){const a=h(-Math.PI*.92,-Math.PI*.08);return{x:Math.cos(a),y:Math.sin(a),speedMultiplier:h(.28,1.35)}}if(n==="scatter"){const a=h(0,Math.PI*2);return{x:Math.cos(a),y:Math.sin(a),speedMultiplier:h(.2,1.55)}}if(n==="heart"){const a=h(0,Math.PI*2),r=16*Math.pow(Math.sin(a),3),s=-(13*Math.cos(a)-5*Math.cos(2*a)-2*Math.cos(3*a)-Math.cos(4*a)),o=Math.hypot(r,s)||1;return{x:r/o,y:s/o,offsetX:r*1.8,offsetY:s*1.8,speedMultiplier:h(.72,1.12)}}const i=h(0,Math.PI*2);return{x:Math.cos(i),y:Math.sin(i),speedMultiplier:Math.sqrt(Math.random())}}class D{constructor(){L(this,"active",!1)}reset(e){this.active=!0,this.x=e.x,this.y=e.y,this.vx=e.vx,this.vy=e.vy,this.size=e.size,this.maxLife=e.life,this.life=e.life,this.startColor=e.startColor,this.endColor=e.endColor,this.textureType=e.textureType,this.config=e.config||null,this.rotation=h(0,Math.PI*2),this.rotationVelocity=e.rotationVelocity,this.secondaryEligible=e.secondaryEligible,this.secondaryBurstConfig=e.secondaryBurstConfig||null,this.secondaryTriggered=!1,this.flickerSeed=h(0,1e3),this.trail=[]}update(e,t){if(!this.active)return null;const i=this.config||t,a=i.windEnabled?i.windX:0,r=i.windEnabled?i.windY:0;return this.vx+=a*e,this.vy+=(i.gravity+r)*e,this.vx*=Math.pow(i.damping,e),this.vy*=Math.pow(i.damping,e),this.x+=this.vx*e,this.y+=this.vy*e,this.rotation+=this.rotationVelocity*e,this.life-=e,i.enableTrails?(this.trail.push({x:this.x,y:this.y,life:this.life}),this.trail.length>8&&this.trail.shift()):this.trail.length=0,i.enableSecondaryBurst&&this.secondaryEligible&&!this.secondaryTriggered&&this.life<this.maxLife*.52?(this.secondaryTriggered=!0,this.active=!1,{x:this.x,y:this.y,source:{startColor:this.startColor,endColor:this.endColor,textureType:this.textureType,size:this.size,maxLife:this.maxLife,config:i,secondaryBurstConfig:this.secondaryBurstConfig}}):(this.life<=0&&(this.active=!1),null)}draw(e,t){if(!this.active)return;const i=this.config||t,a=C(this.life/this.maxLife,0,1),r=1-a,s=M(this.size*i.sizeEndMultiplier,this.size,a),o=C(r+i.colorShiftRate*r*8,0,1),l=j(this.startColor,this.endColor,o);let d=a;i.flicker&&(d*=.72+Math.abs(Math.sin(this.flickerSeed+this.life*.34))*.34),i.enableTrails&&this.trail.length>1&&this.drawTrail(e,l,d,s),e.save(),e.translate(this.x,this.y),e.rotate(this.rotation),e.fillStyle=S(l,d),e.shadowColor=S(l,d),e.shadowBlur=18*i.glowIntensity,H(e,this.textureType||i.textureType,s),e.restore()}drawTrail(e,t,i,a){e.save(),e.lineCap="round",e.lineJoin="round";for(let r=1;r<this.trail.length;r+=1){const s=this.trail[r-1],o=this.trail[r],l=r/this.trail.length;e.strokeStyle=S(t,i*l*.26),e.lineWidth=Math.max(1,a*l*.8),e.beginPath(),e.moveTo(s.x,s.y),e.lineTo(o.x,o.y),e.stroke()}e.restore()}}function H(n,e,t){if(e==="square"){n.fillRect(-t,-t,t*2,t*2);return}if(e==="star"){K(n,5,t*1.45,t*.58);return}if(e==="heart"){_(n,t*1.25);return}n.beginPath(),n.arc(0,0,t,0,Math.PI*2),n.fill()}function K(n,e,t,i){n.beginPath();for(let a=0;a<e*2;a+=1){const r=a%2===0?t:i,s=a*Math.PI/e-Math.PI/2,o=Math.cos(s)*r,l=Math.sin(s)*r;a===0?n.moveTo(o,l):n.lineTo(o,l)}n.closePath(),n.fill()}function _(n,e){n.beginPath(),n.moveTo(0,e*.5),n.bezierCurveTo(e*1.25,-e*.35,e*.65,-e*1.35,0,-e*.62),n.bezierCurveTo(-e*.65,-e*1.35,-e*1.25,-e*.35,0,e*.5),n.fill()}class W{constructor(e,t=1400){this.factory=e,this.pool=Array.from({length:t},()=>new D),this.activeCount=0}spawn(e){const t=this.pool.find(i=>!i.active);return t?(t.reset(e),this.activeCount+=1,!0):!1}addExplosion(e){for(const t of e)this.spawn(t)}update(e,t){const i=[];let a=0;for(const r of this.pool){if(!r.active)continue;const s=r.update(e,t);s&&i.push(s),r.active&&(a+=1)}this.activeCount=a;for(const r of i.slice(0,12))this.addExplosion(this.factory.createSubExplosion(r.x,r.y,r.source))}draw(e,t){for(const i of this.pool)i.draw(e,t)}clear(){for(const e of this.pool)e.active=!1,e.trail.length=0;this.activeCount=0}}class G{constructor(e,t){this.canvas=e,this.ctx=e.getContext("2d",{alpha:!1}),this.backgroundManager=t,this.width=0,this.height=0,this.pixelRatio=1}resize(){const e=this.canvas.getBoundingClientRect();this.pixelRatio=Math.min(window.devicePixelRatio||1,2),this.width=Math.max(1,Math.floor(e.width)),this.height=Math.max(1,Math.floor(e.height)),this.canvas.width=Math.floor(this.width*this.pixelRatio),this.canvas.height=Math.floor(this.height*this.pixelRatio),this.ctx.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0),this.backgroundManager.resize(this.width,this.height)}clearFrame(e,t=!1){if(!e.enableTrails||t){this.backgroundManager.draw(this.ctx,this.width,this.height,e);return}this.ctx.save(),this.ctx.globalCompositeOperation="source-over",this.ctx.fillStyle=`rgba(3, 6, 12, ${e.fadeTrail})`,this.ctx.fillRect(0,0,this.width,this.height),this.ctx.restore()}draw(e,t,i){this.ctx.save(),this.ctx.globalCompositeOperation=e.glowIntensity>0?"lighter":"source-over";for(const a of i)a.draw(this.ctx,e.showLaunchTrail);t.draw(this.ctx,e),this.ctx.restore()}}class k{constructor(e={}){var i,a;const t=J(e.canvasId??e.canvas);if(!t)throw new Error("FireworksSimulator requires a valid canvas or canvasId.");this.canvas=t,this.prefersReducedMotion=e.prefersReducedMotion??((a=(i=window.matchMedia)==null?void 0:i.call(window,"(prefers-reduced-motion: reduce)"))==null?void 0:a.matches)??!1,this.initialConfig=this.createInitialConfig(e.config),this.config={...this.initialConfig},this.eventBus=e.eventBus??new N,this.onConfigChange=e.onConfigChange??(()=>{}),this.onFpsChange=e.onFpsChange??(()=>{}),this.onLowFps=e.onLowFps??(()=>{}),this.advancedMode=!!e.advancedMode,this.createParticles=typeof e.createParticles=="function"?e.createParticles:null,this.backgroundManager=new V,this.renderer=new G(t,this.backgroundManager),this.fireworkFactory=new $(()=>this.config),this.particleSystem=new W(this.fireworkFactory),this.shells=[],this.paused=!1,this.destroyed=!1,this.lastTime=performance.now(),this.autoTimer=0,this.fpsFrames=0,this.fpsElapsed=0,this.lowFpsTime=0,this.warnedAboutPerformance=!1,this.handlePointerDown=this.handlePointerDown.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleResize=this.resize.bind(this),this.handleVisibilityChange=this.handleVisibilityChange.bind(this),this.animate=this.animate.bind(this),e.clickToLaunch!==!1&&this.canvas.addEventListener("pointerdown",this.handlePointerDown),e.keyboard!==!1&&this.canvas.addEventListener("keydown",this.handleKeyDown),e.autoResize!==!1&&window.addEventListener("resize",this.handleResize),e.pauseOnHidden!==!1&&document.addEventListener("visibilitychange",this.handleVisibilityChange),this.resize(),this.renderer.clearFrame(this.config,!0),e.initialLaunch!==!1&&window.setTimeout(()=>{this.destroyed||(this.launch(this.renderer.width*.34,this.renderer.height*.42),this.launch(this.renderer.width*.62,this.renderer.height*.34))},300),this.animationId=requestAnimationFrame(this.animate)}createInitialConfig(e={}){const t={...f,...this.prefersReducedMotion?{particleCount:70,initialSpeedMin:1.4,initialSpeedMax:4.2,autoLaunch:!1,enableTrails:!1}:{},...e};return this.normalizeAutoLaunchIntervalPriority(t,e),p(t)}getConfig(){return{...this.config}}setConfig(e={}){const t={...this.config,...e};return this.normalizeAutoLaunchIntervalPriority(t,e),this.config=p(t),this.notifyConfigChange(),this.renderer.clearFrame(this.config,!0),this.getConfig()}selectPreset(e){var a;if(e==="default")return this.setConfig(f);const t=(a=e==null?void 0:e.startsWith)!=null&&a.call(e,"builtin:")?e.replace("builtin:",""):e,i=w[t];if(!i)throw new Error(`Unknown preset: ${e}`);return this.setConfig({...f,...i.config})}resetConfig(e){return e&&(this.initialConfig=this.createInitialConfig(e)),this.config={...this.initialConfig},this.clearParticles(),this.autoTimer=0,this.warnedAboutPerformance=!1,this.notifyConfigChange(),this.renderer.clearFrame(this.config,!0),this.getConfig()}startAutoPlay(e){const t={autoLaunch:!0};return Number.isFinite(Number(e))&&Object.assign(t,this.normalizeAutoPlayIntervalInput(e)),this.setConfig(t)}stopAutoPlay(){return this.setConfig({autoLaunch:!1})}setAutoPlay(e,t){return e?this.startAutoPlay(t):this.stopAutoPlay()}setAdvancedMode(e,t){this.advancedMode=!!e,typeof t=="function"&&(this.createParticles=t)}setParticleCreator(e){this.createParticles=typeof e=="function"?e:null}clearParticles(){this.particleSystem.clear(),this.shells.length=0,this.renderer.clearFrame(this.config,!0)}launch(e,t,i={}){const a=i.withShell??this.config.showLaunchTrail;this.launchAt(e,t,a)}launchRandom(){const e=h(this.renderer.width*.16,this.renderer.width*.84),t=h(this.renderer.height*.16,this.renderer.height*.62);this.launchAt(e,t,this.config.showLaunchTrail)}pause(){this.paused=!0}resume(){this.paused=!1,this.lastTime=performance.now()}togglePause(){return this.paused?this.resume():this.pause(),this.paused}resize(){this.renderer.resize(),this.renderer.clearFrame(this.config,!0)}setBackgroundImage(e,t,i){this.backgroundManager.setImage(e,()=>{this.setConfig({backgroundType:"image"}),t==null||t()},i)}destroy(){this.destroyed=!0,cancelAnimationFrame(this.animationId),this.canvas.removeEventListener("pointerdown",this.handlePointerDown),this.canvas.removeEventListener("keydown",this.handleKeyDown),window.removeEventListener("resize",this.handleResize),document.removeEventListener("visibilitychange",this.handleVisibilityChange)}on(e,t){return this.eventBus.on(e,t)}launchAt(e,t,i=!0){if(i&&this.config.showLaunchTrail){this.shells.push(this.fireworkFactory.createLaunch(e,this.renderer.height+16,e,t));return}this.particleSystem.addExplosion(this.createExplosionParticles(e,t))}createExplosionParticles(e,t){if(!this.advancedMode||!this.createParticles)return this.fireworkFactory.createExplosion(e,t);const{particles:i,scopedConfig:a}=this.parseAdvancedResult(this.createParticles(this.getCanvasData(e,t)));return Array.isArray(i)?i.map(r=>this.normalizeParticle(r,e,t,a)):[]}parseAdvancedResult(e){return Array.isArray(e)?{particles:e,scopedConfig:null}:e&&typeof e=="object"&&Array.isArray(e.particles)?{particles:e.particles,scopedConfig:e.config?p({...this.config,...e.config}):null}:{particles:[],scopedConfig:null}}getCanvasData(e,t){return{canvas:this.canvas,ctx:this.renderer.ctx,x:e,y:t,width:this.renderer.width,height:this.renderer.height,pixelRatio:this.renderer.pixelRatio,config:this.getConfig(),random:h}}normalizeParticle(e,t,i,a=null){const r=a||this.config,s=e.secondaryBurstConfig&&typeof e.secondaryBurstConfig=="object"?this.normalizeSecondaryBurstConfig(e.secondaryBurstConfig,r):null;return{x:Number.isFinite(e.x)?e.x:t,y:Number.isFinite(e.y)?e.y:i,vx:Number.isFinite(e.vx)?e.vx:0,vy:Number.isFinite(e.vy)?e.vy:0,size:Number.isFinite(e.size)?e.size:r.particleMinSize,life:Number.isFinite(e.life)?e.life:r.particleLife,startColor:e.startColor||e.color||r.colorStart,endColor:e.endColor||e.color||r.colorEnd,textureType:e.textureType||r.textureType,rotationVelocity:Number.isFinite(e.rotationVelocity)?e.rotationVelocity:h(-r.rotationSpeed,r.rotationSpeed),secondaryEligible:!!e.secondaryEligible,secondaryBurstConfig:s,config:a}}normalizeSecondaryBurstConfig(e,t){const i=p({...t,...e,enableSecondaryBurst:!1}),a={enableSecondaryBurst:!1};for(const r of Object.keys(e))r!=="enableSecondaryBurst"&&r in i&&(a[r]=i[r]);return a}animate(e){if(this.destroyed)return;const t=Math.min(50,e-this.lastTime);if(this.lastTime=e,!this.paused){const i=t/16.6667;this.renderer.clearFrame(this.config);for(let a=this.shells.length-1;a>=0;a-=1)if(this.shells[a].update(i)){const r=this.shells.splice(a,1)[0];this.particleSystem.addExplosion(this.createExplosionParticles(r.targetX,r.targetY))}this.particleSystem.update(i,this.config),this.renderer.draw(this.config,this.particleSystem,this.shells),this.config.autoLaunch&&(this.autoTimer+=t,this.autoTimer>=this.getAutoLaunchIntervalMs()&&(this.autoTimer=0,this.launchRandom())),this.updateFps(t)}this.animationId=requestAnimationFrame(this.animate)}updateFps(e){if(this.fpsFrames+=1,this.fpsElapsed+=e,this.fpsElapsed<500)return;const t=Math.round(this.fpsFrames*1e3/this.fpsElapsed);this.onFpsChange(t),this.eventBus.emit("fps:change",t),this.lowFpsTime=t<45?this.lowFpsTime+this.fpsElapsed:0,this.lowFpsTime>2200&&!this.warnedAboutPerformance&&(this.warnedAboutPerformance=!0,this.config.particleCount=Math.max(60,Math.round(this.config.particleCount*.72)),this.notifyConfigChange(),this.onLowFps(this.getConfig()),this.eventBus.emit("performance:low-fps",this.getConfig())),this.fpsFrames=0,this.fpsElapsed=0}notifyConfigChange(){const e=this.getConfig();this.onConfigChange(e),this.eventBus.emit("simulator:config-change",{config:e})}handlePointerDown(e){const t=this.canvas.getBoundingClientRect();this.launch(e.clientX-t.left,e.clientY-t.top)}handleKeyDown(e){e.code==="Space"?(e.preventDefault(),this.togglePause()):e.code==="Enter"&&(e.preventDefault(),this.launchRandom())}handleVisibilityChange(){document.hidden?this.pause():this.lastTime=performance.now()}getAutoLaunchIntervalMs(){const e=Number(this.config.autoLaunchIntervalSeconds);return Number.isFinite(e)?e*1e3:this.config.autoInterval}normalizeAutoPlayIntervalInput(e){const t=Number(e);return t>10?{autoInterval:t}:{autoLaunchIntervalSeconds:t}}normalizeAutoLaunchIntervalPriority(e,t){"autoInterval"in t&&!("autoLaunchIntervalSeconds"in t)&&delete e.autoLaunchIntervalSeconds,"autoLaunchIntervalSeconds"in t&&!("autoInterval"in t)&&delete e.autoInterval}}function J(n){return typeof n=="string"?document.getElementById(n):n}function T(n,e={}){return new k(Q(n,e))}function F(n,e={}){return T(n,e)}const R={create:T,init:F,FireworksSimulator:k,builtInPresets:w,defaultConfig:f,sanitizeConfig:p};typeof window<"u"&&(window.SparkFireworks=R);function Q(n,e){const t=typeof n=="string"?{canvasId:n}:{canvas:n};return e&&typeof e=="object"&&("config"in e||"onConfigChange"in e||"onFpsChange"in e||"onLowFps"in e||"clickToLaunch"in e||"keyboard"in e||"autoResize"in e||"pauseOnHidden"in e||"initialLaunch"in e||"advancedMode"in e||"createParticles"in e)?{...e,...t}:{...t,config:e}}c.FireworksSimulator=k,c.builtInPresets=w,c.create=T,c.default=R,c.defaultConfig=f,c.init=F,c.sanitizeConfig=p,Object.defineProperties(c,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});