@basementuniverse/particles-2d 1.0.1

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,357 @@
1
+ import { vec2 } from '@basementuniverse/vec';
2
+ export type RandomRange<T extends number | vec2 = number> = {
3
+ min: T;
4
+ max: T;
5
+ };
6
+ export type Color = {
7
+ r: number;
8
+ g: number;
9
+ b: number;
10
+ a?: number;
11
+ };
12
+ export declare class ParticleSystem {
13
+ particles: Particle[];
14
+ emitters: Emitter[];
15
+ attractors: Attractor[];
16
+ forceFields: ForceField[];
17
+ colliders: Collider[];
18
+ update(dt: number): void;
19
+ draw(context: CanvasRenderingContext2D): void;
20
+ }
21
+ declare const PARTICLE_DEFAULT_UPDATE_TYPES: string[];
22
+ type ParticleDefaultUpdateTypes = (typeof PARTICLE_DEFAULT_UPDATE_TYPES)[number][];
23
+ declare const PARTICLE_DEFAULT_DRAW_TYPES: string[];
24
+ type ParticleDefaultDrawTypes = (typeof PARTICLE_DEFAULT_DRAW_TYPES)[number][];
25
+ export type ParticleOptions = {
26
+ /**
27
+ * Should this particle be affected by attractors
28
+ */
29
+ useAttractors: boolean;
30
+ /**
31
+ * Should this particle be affected by force fields
32
+ */
33
+ useForceFields: boolean;
34
+ /**
35
+ * Should this particle be affected by colliders
36
+ */
37
+ useColliders: boolean;
38
+ /**
39
+ * What kind of default update logic to apply. This can be 'all', 'none', or
40
+ * an array of specific updates to apply:
41
+ *
42
+ * - 'none': no default update, only custom updates will be applied
43
+ * - 'all': apply all updates, including custom updates if provided
44
+ * - 'age': apply age updates and dispose when lifespan is reached
45
+ * - 'physics': apply physics updates (attractors, force fields, colliders)
46
+ * - 'direction': apply direction updates (calculate rotation based on
47
+ * velocity)
48
+ * - 'position': apply position updates (integrate velocity over time)
49
+ */
50
+ defaultUpdates: 'none' | 'all' | ParticleDefaultUpdateTypes;
51
+ update?: (system: ParticleSystem, dt: number) => void;
52
+ /**
53
+ * What kind of default rendering logic to apply. This can be 'all', 'none',
54
+ * or an array of specific draws to apply:
55
+ *
56
+ * - 'none': no default rendering, only custom rendering will be applied
57
+ * - 'all': apply all rendering logic, including custom rendering if provided
58
+ * - 'transforms': apply basic translation transform, and rotation transform
59
+ * if using 'image' style
60
+ * - 'fade': apply fade in/out effects based on particle age
61
+ * - 'styles': render particles using one of the default styles (e.g. 'dot',
62
+ * 'line', etc.)
63
+ */
64
+ defaultDraws: 'none' | 'all' | ParticleDefaultDrawTypes;
65
+ /**
66
+ * Called before default style rendering, useful for setting context state
67
+ * (e.g. shadow, global alpha, etc.) before drawing particles
68
+ */
69
+ preDraw?: (system: ParticleSystem, context: CanvasRenderingContext2D) => void;
70
+ /**
71
+ * Called after default style rendering, useful for rendering additional
72
+ * effects or overlays after default styles have been drawn
73
+ */
74
+ postDraw?: (system: ParticleSystem, context: CanvasRenderingContext2D) => void;
75
+ };
76
+ type GlowStyle = {
77
+ color: Color | string | Color[] | string[];
78
+ amount: number;
79
+ };
80
+ type FadeStyle = {
81
+ in: number;
82
+ out: number;
83
+ };
84
+ export type ParticleStyle = ({
85
+ style: 'dot';
86
+ color: Color | string | Color[] | string[];
87
+ glow?: GlowStyle;
88
+ } | {
89
+ style: 'radial';
90
+ color: Color | string | Color[] | string[];
91
+ } | {
92
+ style: 'line';
93
+ color: Color | string | Color[] | string[];
94
+ rotationOffset?: number;
95
+ glow?: GlowStyle;
96
+ } | {
97
+ style: 'image';
98
+ image: HTMLImageElement;
99
+ rotationOffset?: number;
100
+ }) & {
101
+ fade?: FadeStyle;
102
+ };
103
+ export declare class Particle {
104
+ /**
105
+ * Initial position of the particle
106
+ */
107
+ position: vec2;
108
+ /**
109
+ * Initial velocity of the particle
110
+ */
111
+ velocity: vec2;
112
+ /**
113
+ * Size of the particle. This is used differently based on the style:
114
+ *
115
+ * - 'dot' style: we use the maximum of x and y as the radius
116
+ * - 'line' style: x is the length of the line, y is the line width
117
+ * - 'radial' style: we use the maximum of x and y as the radius
118
+ * - 'image' style: x and y are the width and height of the image
119
+ */
120
+ size: vec2;
121
+ /**
122
+ * Rotation of the particle in radians
123
+ *
124
+ * _(Note: not used for 'dot' and 'radial' styles)_
125
+ *
126
+ * If this is null, we calculate rotation based on velocity
127
+ */
128
+ rotation: number | null;
129
+ /**
130
+ * Lifespan of the particle in seconds
131
+ */
132
+ lifespan: number;
133
+ age: number;
134
+ style: ParticleStyle | null;
135
+ private actualRotation;
136
+ private actualColor;
137
+ private actualColorTransparent;
138
+ private actualGlowColor;
139
+ private options;
140
+ private _disposed;
141
+ constructor(
142
+ /**
143
+ * Initial position of the particle
144
+ */
145
+ position: vec2,
146
+ /**
147
+ * Initial velocity of the particle
148
+ */
149
+ velocity: vec2,
150
+ /**
151
+ * Size of the particle. This is used differently based on the style:
152
+ *
153
+ * - 'dot' style: we use the maximum of x and y as the radius
154
+ * - 'line' style: x is the length of the line, y is the line width
155
+ * - 'radial' style: we use the maximum of x and y as the radius
156
+ * - 'image' style: x and y are the width and height of the image
157
+ */
158
+ size: vec2,
159
+ /**
160
+ * Rotation of the particle in radians
161
+ *
162
+ * _(Note: not used for 'dot' and 'radial' styles)_
163
+ *
164
+ * If this is null, we calculate rotation based on velocity
165
+ */
166
+ rotation?: number | null,
167
+ /**
168
+ * Lifespan of the particle in seconds
169
+ */
170
+ lifespan?: number,
171
+ /**
172
+ * Style options for the particle. This can be used to define a default
173
+ * rendering style and associated settings for the style
174
+ *
175
+ * The style can be one of:
176
+ *
177
+ * - 'dot': a simple dot with a color and optional glow
178
+ * - 'radial': a radial gradient with a color that fades to transparent
179
+ * - 'line': a line segment with a color, optional glow, and optional
180
+ * rotation (the rotation can be relative or absolute)
181
+ * - 'image': an image with an optional rotation (the rotation can be
182
+ * relative or absolute)
183
+ *
184
+ * If this is null, the particle will use the custom rendering hook if
185
+ * provided, or it will be invisible if no custom rendering is provided
186
+ *
187
+ * Omit this field or set it to undefined to use the default style
188
+ */
189
+ style?: Partial<ParticleStyle> | null,
190
+ /**
191
+ * Provide custom update logic and rendering logic here
192
+ */
193
+ options?: Partial<ParticleOptions>);
194
+ get disposed(): boolean;
195
+ get normalisedLifeRemaining(): number;
196
+ update(system: ParticleSystem, dt: number): void;
197
+ draw(system: ParticleSystem, context: CanvasRenderingContext2D): void;
198
+ }
199
+ export type EmitterOptions = {
200
+ /**
201
+ * Particle definition. Defines the type of particles emitted by the emitter
202
+ */
203
+ particles: {
204
+ /**
205
+ * How to distribute initial particle positions within the emitter area:
206
+ *
207
+ * - 'uniform': uniform distribution within the area
208
+ * - 'normal': normal distribution from the center of the emitter area
209
+ * - a custom function which takes the number of particles emitted so far in
210
+ * the current round of emission and returns a position vector
211
+ */
212
+ position: 'uniform' | 'normal' | ((n: number) => vec2);
213
+ /**
214
+ * The initial speed of new particles. This affects the initial velocity
215
+ * applied to each particle
216
+ *
217
+ * This can be one of:
218
+ *
219
+ * - a fixed speed
220
+ * - a range of random speeds (min and max)
221
+ * - a custom function which takes the number of particles emitted so far in
222
+ * the current round of emission and returns a speed value
223
+ */
224
+ speed: number | RandomRange | ((n: number) => number);
225
+ /**
226
+ * The initial direction of new particles. This affects the initial velocity
227
+ * applied to each particle
228
+ *
229
+ * This can be one of:
230
+ *
231
+ * - a fixed angle in radians
232
+ * - a range of random angles (min and max)
233
+ * - a custom function which takes the number of particles emitted so far in
234
+ * the current round of emission and returns a direction value in radians
235
+ */
236
+ direction: number | RandomRange | ((n: number) => number);
237
+ /**
238
+ * The initial size of new particles. This can be one of:
239
+ *
240
+ * - a fixed size vector (x, y)
241
+ * - a range of random sizes (min and max)
242
+ * - a custom function which takes the number of particles emitted so far in
243
+ * the current round of emission and returns a size vector (x, y)
244
+ */
245
+ size: vec2 | RandomRange | ((n: number) => vec2);
246
+ /**
247
+ * The initial rotation of new particles in radians. This can be one of:
248
+ *
249
+ * - a fixed rotation in radians
250
+ * - a range of random rotations (min and max)
251
+ * - a custom function which takes the number of particles emitted so far in
252
+ * the current round of emission and returns a rotation value in radians
253
+ * - null, meaning the particles' rotation will be calculated based on their
254
+ * velocity vector
255
+ */
256
+ rotation: number | RandomRange | ((n: number) => number) | null;
257
+ /**
258
+ * The lifespan of new particles in seconds. This can be one of:
259
+ *
260
+ * - a fixed lifespan in seconds
261
+ * - a range of random lifespans (min and max)
262
+ * - a custom function which takes the number of particles emitted so far in
263
+ * the current round of emission and returns a lifespan value in seconds
264
+ */
265
+ lifespan: number | RandomRange | ((n: number) => number);
266
+ /**
267
+ * The style options for new particles
268
+ */
269
+ style?: Partial<ParticleStyle> | null;
270
+ /**
271
+ * Additional options for the particle, such as custom update logic or
272
+ * rendering logic
273
+ */
274
+ options?: Partial<ParticleOptions>;
275
+ };
276
+ /**
277
+ * Controls the rate at which particles are emitted. This can be one of:
278
+ *
279
+ * - 'rate': emit some number of particles per second
280
+ * - 'burst': emit a random number of particles at once
281
+ * - 'custom': provide a custom function which returns the number of particles
282
+ * to emit on each update
283
+ */
284
+ emission: {
285
+ type: 'rate';
286
+ rate: number | RandomRange;
287
+ } | {
288
+ type: 'burst';
289
+ n: number | RandomRange;
290
+ delay?: number;
291
+ } | {
292
+ type: 'custom';
293
+ f: () => number;
294
+ };
295
+ };
296
+ export declare class Emitter {
297
+ position: vec2;
298
+ size: vec2;
299
+ lifespan: number;
300
+ private static readonly RANDOM_RATE_CHANGE_INTERVAL;
301
+ age: number;
302
+ totalParticlesEmitted: number;
303
+ private options;
304
+ private _disposed;
305
+ private currentRate;
306
+ private lastRateChange;
307
+ private particlesToEmit;
308
+ constructor(position: vec2, size?: vec2, lifespan?: number, options?: Partial<EmitterOptions>);
309
+ get disposed(): boolean;
310
+ update(system: ParticleSystem, dt: number): void;
311
+ private emitParticles;
312
+ private createParticle;
313
+ }
314
+ export declare class Attractor {
315
+ position: vec2;
316
+ range: number;
317
+ force: number;
318
+ falloff: number;
319
+ lifespan: number;
320
+ age: number;
321
+ private _disposed;
322
+ constructor(position: vec2, range?: number, force?: number, falloff?: number, lifespan?: number);
323
+ get disposed(): boolean;
324
+ applyForce(particle: Particle, dt: number): void;
325
+ update(dt: number): void;
326
+ }
327
+ export declare class ForceField {
328
+ force: vec2;
329
+ lifespan: number;
330
+ age: number;
331
+ private _disposed;
332
+ constructor(force?: vec2, lifespan?: number);
333
+ get disposed(): boolean;
334
+ applyForce(particle: Particle, dt: number): void;
335
+ update(dt: number): void;
336
+ }
337
+ export type ColliderGeometry = {
338
+ type: 'circle';
339
+ position: vec2;
340
+ radius: number;
341
+ } | {
342
+ type: 'rectangle';
343
+ position: vec2;
344
+ size: vec2;
345
+ rotation?: number;
346
+ } | {
347
+ type: 'polygon';
348
+ vertices: vec2[];
349
+ };
350
+ export declare class Collider {
351
+ geometry: ColliderGeometry;
352
+ restitution: number;
353
+ friction: number;
354
+ constructor(geometry: ColliderGeometry, restitution?: number, friction?: number);
355
+ handleCollision(particle: Particle): void;
356
+ }
357
+ export {};