fireworks-engine 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.
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "fireworks-engine",
3
+ "version": "1.0.0",
4
+ "license": "BSD-2-Clause",
5
+ "description": "Reusable canvas fireworks runtime package for standalone integrations and demos.",
6
+ "author": "coinexplain",
7
+ "homepage": "https://github.com/coinexplain/fireworks-engine#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/coinexplain/fireworks-engine/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/coinexplain/fireworks-engine.git"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "index.d.ts",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "keywords": [
22
+ "fireworks-engine",
23
+ "fireworks",
24
+ "confetti",
25
+ "canvas",
26
+ "animation",
27
+ "effects",
28
+ "particles",
29
+ "standalone"
30
+ ],
31
+ "type": "module",
32
+ "main": "./src/index.js",
33
+ "types": "./index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./index.d.ts",
37
+ "default": "./src/index.js"
38
+ }
39
+ },
40
+ "sideEffects": false,
41
+ "scripts": {
42
+ "smoke": "node --check src/index.js && node --input-type=module -e \"const mod = await import('./src/index.js'); if (typeof mod.createFireworksEngine !== 'function' || typeof mod.resolveFireworksEnginePreset !== 'function' || typeof mod.default !== 'function') throw new Error('package smoke failed'); console.log('package smoke ok');\"",
43
+ "typecheck": "tsc --noEmit --project tsconfig.types.json",
44
+ "test": "npm run smoke && npm run typecheck"
45
+ },
46
+ "devDependencies": {
47
+ "typescript": "^5.9.3"
48
+ }
49
+ }
@@ -0,0 +1,262 @@
1
+ import {
2
+ DEFAULT_BURST_FORMS_COUNT,
3
+ DEFAULT_BURST_FORMS_SPEED_MIN,
4
+ DEFAULT_BURST_FORMS_SPEED_MAX,
5
+ DEFAULT_BURST_FORMS_GRAVITY,
6
+ DEFAULT_BURST_FORMS_DIRECTION_DEG,
7
+ DEFAULT_BURST_FORMS_CONE_DEG,
8
+ DEFAULT_BURST_SPARK_GRAVITY,
9
+ DEFAULT_BURST_SPARK_DIRECTION_DEG,
10
+ DEFAULT_BURST_SPARK_CONE_DEG,
11
+ DEFAULT_BURST_FORMS_LIFE_MIN,
12
+ DEFAULT_BURST_FORMS_LIFE_MAX,
13
+ DEFAULT_BURST_FORMS_SIZE_MIN,
14
+ DEFAULT_BURST_FORMS_SIZE_MAX,
15
+ DEFAULT_BURST_FORMS_SPIN,
16
+ DEFAULT_BURST_FORMS_SPIN_COVERAGE,
17
+ DEFAULT_BURST_FORMS_SPIN_DURATION,
18
+ DEFAULT_BURST_FORMS_SPIN_PAUSE,
19
+ DEFAULT_BURST_FORMS_HIGHLIGHT,
20
+ DEFAULT_BURST_FORMS_HIGHLIGHT_ANGLE_DEG,
21
+ DEFAULT_BURST_FORMS_HIGHLIGHT_WIDTH,
22
+ DEFAULT_BURST_FORMS_WOBBLE,
23
+ DEFAULT_BURST_PALETTE,
24
+ BURST_SPARK_COUNT_BASE,
25
+ BURST_SPARK_SPEED_MIN_BASE,
26
+ BURST_SPARK_SPEED_MAX_BASE,
27
+ BURST_SPARK_LIFE_MIN_BASE,
28
+ BURST_SPARK_LIFE_MAX_BASE,
29
+ BURST_SPARK_LENGTH_BASE,
30
+ DEFAULT_BURST_SPARK_TAIL_SCALE,
31
+ BURST_SPARK_THICKNESS_BASE,
32
+ } from './defaults.js';
33
+ import {
34
+ resolveScaledOverride,
35
+ resolveScaledInteger,
36
+ resolveScaledInverseOverride,
37
+ resolveSizeScale,
38
+ resolveEnvelopeTimeScale,
39
+ } from './mathResolvers.js';
40
+ import {
41
+ resolveBurstEntityToggle,
42
+ resolveBurstFormShape,
43
+ normalizeBurstFormGlyph,
44
+ resolveBurstColorMode,
45
+ resolveBurstFlashColorMode,
46
+ resolveBurstSparkShape,
47
+ } from './valueNormalizers.js';
48
+ import { normalizeHexColor } from './colorUtils.js';
49
+
50
+ const MIN_COUNT = 1;
51
+ const TWINKLE_CLOUD_PARTICLE_SIZE_SCALE_FACTOR = 0.5;
52
+
53
+ export function resolveTwinkleParticleSizeScale(sizeScale) {
54
+ return 1 + ((sizeScale - 1) * TWINKLE_CLOUD_PARTICLE_SIZE_SCALE_FACTOR);
55
+ }
56
+
57
+ function resolveBurstFormsConfig(formsInput, formsCurrent, normalizedOptions, sizeScale, envelopeTimeScale, simple) {
58
+ return {
59
+ enabled: resolveBurstEntityToggle(formsInput.enabled) ?? formsCurrent.enabled,
60
+ count: resolveScaledInteger(
61
+ formsInput.count ?? formsCurrent.count,
62
+ DEFAULT_BURST_FORMS_COUNT,
63
+ MIN_COUNT,
64
+ sizeScale,
65
+ ),
66
+ speedMin: resolveScaledOverride(
67
+ formsInput.speedMin ?? formsCurrent.speedMin,
68
+ DEFAULT_BURST_FORMS_SPEED_MIN,
69
+ envelopeTimeScale,
70
+ ),
71
+ speedMax: resolveScaledOverride(
72
+ formsInput.speedMax ?? formsCurrent.speedMax,
73
+ DEFAULT_BURST_FORMS_SPEED_MAX,
74
+ envelopeTimeScale,
75
+ ),
76
+ gravity: formsInput.gravity ?? formsCurrent.gravity ?? DEFAULT_BURST_FORMS_GRAVITY,
77
+ lifeMin: resolveScaledOverride(
78
+ formsInput.lifeMin ?? formsCurrent.lifeMin,
79
+ DEFAULT_BURST_FORMS_LIFE_MIN,
80
+ envelopeTimeScale,
81
+ ),
82
+ lifeMax: resolveScaledOverride(
83
+ formsInput.lifeMax ?? formsCurrent.lifeMax,
84
+ DEFAULT_BURST_FORMS_LIFE_MAX,
85
+ envelopeTimeScale,
86
+ ),
87
+ sizeMin: resolveScaledOverride(
88
+ formsInput.sizeMin ?? formsCurrent.sizeMin,
89
+ DEFAULT_BURST_FORMS_SIZE_MIN,
90
+ sizeScale,
91
+ ),
92
+ sizeMax: resolveScaledOverride(
93
+ formsInput.sizeMax ?? formsCurrent.sizeMax,
94
+ DEFAULT_BURST_FORMS_SIZE_MAX,
95
+ sizeScale,
96
+ ),
97
+ spin: formsInput.spin ?? formsCurrent.spin ?? DEFAULT_BURST_FORMS_SPIN,
98
+ spinCoverage: formsInput.spinCoverage ?? formsCurrent.spinCoverage ?? DEFAULT_BURST_FORMS_SPIN_COVERAGE,
99
+ spinDuration: resolveScaledOverride(
100
+ formsInput.spinDuration ?? formsCurrent.spinDuration,
101
+ DEFAULT_BURST_FORMS_SPIN_DURATION,
102
+ envelopeTimeScale,
103
+ ),
104
+ spinPause: resolveScaledOverride(
105
+ formsInput.spinPause ?? formsCurrent.spinPause,
106
+ DEFAULT_BURST_FORMS_SPIN_PAUSE,
107
+ envelopeTimeScale,
108
+ ),
109
+ highlight: formsInput.highlight ?? formsCurrent.highlight ?? DEFAULT_BURST_FORMS_HIGHLIGHT,
110
+ highlightAngleDeg: formsInput.highlightAngleDeg
111
+ ?? formsCurrent.highlightAngleDeg
112
+ ?? DEFAULT_BURST_FORMS_HIGHLIGHT_ANGLE_DEG,
113
+ highlightWidth: formsInput.highlightWidth
114
+ ?? formsCurrent.highlightWidth
115
+ ?? DEFAULT_BURST_FORMS_HIGHLIGHT_WIDTH,
116
+ wobble: formsInput.wobble ?? formsCurrent.wobble ?? DEFAULT_BURST_FORMS_WOBBLE,
117
+ directionDeg: formsInput.directionDeg ?? formsCurrent.directionDeg ?? DEFAULT_BURST_FORMS_DIRECTION_DEG,
118
+ coneDeg: formsInput.coneDeg ?? formsCurrent.coneDeg ?? DEFAULT_BURST_FORMS_CONE_DEG,
119
+ palette: formsInput.palette
120
+ ?? normalizedOptions.palette
121
+ ?? formsCurrent.palette
122
+ ?? DEFAULT_BURST_PALETTE,
123
+ simple,
124
+ shape: resolveBurstFormShape(formsInput.shape ?? formsCurrent.shape),
125
+ glyph: normalizeBurstFormGlyph(formsInput.glyph, formsCurrent.glyph),
126
+ colorMode: resolveBurstColorMode(formsInput.colorMode ?? formsCurrent.colorMode),
127
+ singleColor: normalizeHexColor(formsInput.singleColor, formsCurrent.singleColor),
128
+ };
129
+ }
130
+
131
+ function resolveBurstSparksConfig(sparksInput, sparksCurrent, formsCurrent, burstForms, sizeScale, envelopeTimeScale, simple) {
132
+ return {
133
+ enabled: resolveBurstEntityToggle(sparksInput.enabled) ?? sparksCurrent.enabled,
134
+ count: resolveScaledInteger(
135
+ sparksInput.count ?? sparksCurrent.count,
136
+ BURST_SPARK_COUNT_BASE,
137
+ MIN_COUNT,
138
+ sizeScale,
139
+ ),
140
+ speedMin: resolveScaledOverride(
141
+ sparksInput.speedMin ?? (sparksCurrent.speedMin ?? formsCurrent.speedMin),
142
+ BURST_SPARK_SPEED_MIN_BASE,
143
+ envelopeTimeScale,
144
+ ),
145
+ speedMax: resolveScaledOverride(
146
+ sparksInput.speedMax ?? (sparksCurrent.speedMax ?? formsCurrent.speedMax),
147
+ BURST_SPARK_SPEED_MAX_BASE,
148
+ envelopeTimeScale,
149
+ ),
150
+ gravity: sparksInput.gravity ?? sparksCurrent.gravity ?? formsCurrent.gravity ?? DEFAULT_BURST_SPARK_GRAVITY,
151
+ lifeMin: resolveScaledOverride(
152
+ sparksInput.lifeMin ?? (sparksCurrent.lifeMin ?? formsCurrent.lifeMin),
153
+ BURST_SPARK_LIFE_MIN_BASE,
154
+ envelopeTimeScale,
155
+ ),
156
+ lifeMax: resolveScaledOverride(
157
+ sparksInput.lifeMax ?? (sparksCurrent.lifeMax ?? formsCurrent.lifeMax),
158
+ BURST_SPARK_LIFE_MAX_BASE,
159
+ envelopeTimeScale,
160
+ ),
161
+ directionDeg: sparksInput.directionDeg ?? sparksCurrent.directionDeg ?? DEFAULT_BURST_SPARK_DIRECTION_DEG,
162
+ coneDeg: sparksInput.coneDeg ?? sparksCurrent.coneDeg ?? DEFAULT_BURST_SPARK_CONE_DEG,
163
+ palette: sparksInput.palette ?? sparksCurrent.palette ?? burstForms.palette,
164
+ simple,
165
+ length: resolveScaledOverride(sparksInput.length ?? sparksCurrent.length, BURST_SPARK_LENGTH_BASE, sizeScale),
166
+ tailScale: sparksInput.tailScale ?? sparksCurrent.tailScale ?? DEFAULT_BURST_SPARK_TAIL_SCALE,
167
+ thickness: resolveScaledOverride(sparksInput.thickness ?? sparksCurrent.thickness, BURST_SPARK_THICKNESS_BASE, sizeScale),
168
+ shape: resolveBurstSparkShape(sparksInput.shape ?? sparksCurrent.shape),
169
+ colorMode: resolveBurstColorMode(
170
+ sparksInput.colorMode ?? sparksCurrent.colorMode ?? formsCurrent.colorMode,
171
+ ),
172
+ singleColor: normalizeHexColor(
173
+ sparksInput.singleColor ?? sparksCurrent.singleColor ?? formsCurrent.singleColor,
174
+ formsCurrent.singleColor,
175
+ ),
176
+ };
177
+ }
178
+
179
+ function resolveBurstCenterFlashConfig(flashInput, flashCurrent, sizeScale) {
180
+ return {
181
+ enabled: resolveBurstEntityToggle(flashInput.enabled) ?? flashCurrent.enabled,
182
+ startSize: resolveScaledOverride(flashInput.startSize, flashCurrent.startSize, sizeScale),
183
+ endSize: resolveScaledOverride(flashInput.endSize, flashCurrent.endSize, sizeScale),
184
+ peakAlpha: flashInput.peakAlpha ?? flashCurrent.peakAlpha,
185
+ attack: flashInput.attack ?? flashCurrent.attack,
186
+ fade: flashInput.fade ?? flashCurrent.fade,
187
+ colorMode: resolveBurstFlashColorMode(flashInput.colorMode ?? flashCurrent.colorMode),
188
+ color: normalizeHexColor(flashInput.color, flashCurrent.color),
189
+ innerStop: flashInput.innerStop ?? flashCurrent.innerStop,
190
+ };
191
+ }
192
+
193
+ function resolveBurstTwinkleCloudConfig(twinkleInput, twinkleCurrent, sizeScale, envelopeTimeScale) {
194
+ const particleSizeScale = resolveTwinkleParticleSizeScale(sizeScale);
195
+ return {
196
+ enabled: resolveBurstEntityToggle(twinkleInput.enabled) ?? twinkleCurrent.enabled,
197
+ count: resolveScaledInteger(twinkleInput.count, twinkleCurrent.count, MIN_COUNT, sizeScale),
198
+ radius: resolveScaledOverride(twinkleInput.radius, twinkleCurrent.radius, sizeScale),
199
+ startDelay: resolveScaledOverride(twinkleInput.startDelay, twinkleCurrent.startDelay, envelopeTimeScale),
200
+ duration: resolveScaledOverride(twinkleInput.duration, twinkleCurrent.duration, envelopeTimeScale),
201
+ twinkleLife: resolveScaledOverride(twinkleInput.twinkleLife, twinkleCurrent.twinkleLife, envelopeTimeScale),
202
+ brightness: twinkleInput.brightness ?? twinkleCurrent.brightness,
203
+ size: resolveScaledOverride(twinkleInput.size, twinkleCurrent.size, particleSizeScale),
204
+ spawnSpread: resolveScaledInverseOverride(twinkleInput.spawnSpread, twinkleCurrent.spawnSpread, sizeScale),
205
+ };
206
+ }
207
+
208
+ export function buildBurstOptions(normalizedOptions = {}, currentConfig = {}) {
209
+ const safeOptions = normalizedOptions ?? {};
210
+ const sizeScale = resolveSizeScale(safeOptions.sizeScale, currentConfig.sizeScale ?? 1);
211
+ const envelopeTimeScale = resolveEnvelopeTimeScale(sizeScale);
212
+ const simple = !!safeOptions.simple;
213
+ const {
214
+ burstForms: formsInput = {},
215
+ burstSparks: sparksInput = {},
216
+ burstCenterFlash: flashInput = {},
217
+ burstTwinkleCloud: twinkleInput = {},
218
+ } = safeOptions;
219
+
220
+ const formsCurrent = currentConfig.burstForms ?? {};
221
+ const sparksCurrent = currentConfig.burstSparks ?? {};
222
+ const flashCurrent = currentConfig.burstCenterFlash ?? {};
223
+ const twinkleCurrent = currentConfig.burstTwinkleCloud ?? {};
224
+
225
+ const burstForms = resolveBurstFormsConfig(
226
+ formsInput,
227
+ formsCurrent,
228
+ safeOptions,
229
+ sizeScale,
230
+ envelopeTimeScale,
231
+ simple,
232
+ );
233
+ const burstSparks = resolveBurstSparksConfig(
234
+ sparksInput,
235
+ sparksCurrent,
236
+ formsCurrent,
237
+ burstForms,
238
+ sizeScale,
239
+ envelopeTimeScale,
240
+ simple,
241
+ );
242
+ const burstCenterFlash = resolveBurstCenterFlashConfig(
243
+ flashInput,
244
+ flashCurrent,
245
+ sizeScale,
246
+ );
247
+ const burstTwinkleCloud = resolveBurstTwinkleCloudConfig(
248
+ twinkleInput,
249
+ twinkleCurrent,
250
+ sizeScale,
251
+ envelopeTimeScale,
252
+ );
253
+
254
+ return {
255
+ sizeScale,
256
+ simple,
257
+ burstForms,
258
+ burstSparks,
259
+ burstCenterFlash,
260
+ burstTwinkleCloud,
261
+ };
262
+ }