@basmilius/sparkle 2.1.0 → 2.2.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/dist/index.d.mts +306 -459
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1106 -848
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/aurora/index.ts +9 -3
- package/src/aurora/layer.ts +57 -29
- package/src/balloons/index.ts +9 -3
- package/src/balloons/layer.ts +50 -19
- package/src/bubbles/index.ts +9 -3
- package/src/bubbles/layer.ts +30 -17
- package/src/canvas.ts +12 -0
- package/src/color.ts +11 -2
- package/src/confetti/index.ts +15 -3
- package/src/confetti/layer.ts +8 -5
- package/src/confetti/particle.ts +12 -11
- package/src/donuts/consts.ts +2 -2
- package/src/donuts/index.ts +9 -3
- package/src/donuts/layer.ts +43 -12
- package/src/effect.ts +107 -0
- package/src/fade.ts +87 -0
- package/src/fireflies/index.ts +9 -3
- package/src/fireflies/layer.ts +26 -9
- package/src/fireflies/particle.ts +2 -2
- package/src/firepit/index.ts +9 -3
- package/src/firepit/layer.ts +26 -7
- package/src/fireworks/create-explosion.ts +237 -0
- package/src/fireworks/explosion.ts +1 -1
- package/src/fireworks/index.ts +15 -3
- package/src/fireworks/layer.ts +55 -304
- package/src/fireworks/spark.ts +2 -2
- package/src/fireworks/types.ts +2 -2
- package/src/glitter/index.ts +9 -4
- package/src/glitter/layer.ts +15 -7
- package/src/glitter/types.ts +10 -0
- package/src/index.ts +3 -4
- package/src/lanterns/index.ts +9 -4
- package/src/lanterns/layer.ts +22 -10
- package/src/lanterns/types.ts +8 -0
- package/src/layer.ts +13 -11
- package/src/leaves/index.ts +9 -4
- package/src/leaves/layer.ts +21 -14
- package/src/leaves/types.ts +9 -0
- package/src/lightning/index.ts +9 -4
- package/src/lightning/layer.ts +4 -4
- package/src/lightning/system.ts +3 -3
- package/src/lightning/types.ts +10 -2
- package/src/matrix/index.ts +9 -4
- package/src/matrix/layer.ts +15 -7
- package/src/matrix/types.ts +9 -0
- package/src/orbits/index.ts +9 -4
- package/src/orbits/layer.ts +51 -21
- package/src/orbits/types.ts +12 -1
- package/src/particles/index.ts +9 -3
- package/src/particles/layer.ts +55 -12
- package/src/petals/index.ts +9 -3
- package/src/petals/layer.ts +29 -13
- package/src/plasma/index.ts +9 -3
- package/src/plasma/layer.ts +21 -6
- package/src/rain/index.ts +9 -3
- package/src/rain/layer.ts +30 -8
- package/src/sandstorm/index.ts +9 -3
- package/src/sandstorm/layer.ts +26 -9
- package/src/scene.ts +201 -0
- package/src/shooting-stars/system.ts +26 -24
- package/src/shooting-stars/types.ts +2 -1
- package/src/simulation-canvas.ts +40 -4
- package/src/snow/index.ts +9 -3
- package/src/snow/layer.ts +24 -11
- package/src/sparklers/index.ts +13 -3
- package/src/sparklers/layer.ts +61 -15
- package/src/stars/index.ts +9 -3
- package/src/stars/layer.ts +28 -22
- package/src/streamers/index.ts +9 -3
- package/src/streamers/layer.ts +18 -6
- package/src/streamers/types.ts +1 -1
- package/src/waves/index.ts +9 -3
- package/src/waves/layer.ts +42 -45
- package/src/waves/types.ts +1 -0
- package/src/wormhole/index.ts +9 -3
- package/src/wormhole/layer.ts +22 -6
- package/src/aurora/simulation.ts +0 -19
- package/src/balloons/simulation.ts +0 -19
- package/src/bubbles/simulation.ts +0 -20
- package/src/confetti/simulation.ts +0 -27
- package/src/donuts/simulation.ts +0 -25
- package/src/fireflies/simulation.ts +0 -18
- package/src/firepit/simulation.ts +0 -17
- package/src/fireworks/simulation.ts +0 -18
- package/src/glitter/simulation.ts +0 -19
- package/src/lanterns/simulation.ts +0 -17
- package/src/layered.ts +0 -185
- package/src/leaves/simulation.ts +0 -18
- package/src/lightning/simulation.ts +0 -17
- package/src/matrix/simulation.ts +0 -18
- package/src/orbits/simulation.ts +0 -19
- package/src/particles/simulation.ts +0 -26
- package/src/petals/simulation.ts +0 -18
- package/src/plasma/simulation.ts +0 -17
- package/src/rain/simulation.ts +0 -21
- package/src/sandstorm/simulation.ts +0 -18
- package/src/snow/simulation.ts +0 -17
- package/src/sparklers/simulation.ts +0 -30
- package/src/stars/simulation.ts +0 -22
- package/src/streamers/simulation.ts +0 -16
- package/src/waves/simulation.ts +0 -18
- package/src/wormhole/simulation.ts +0 -17
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basmilius/sparkle",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"email": "bas@mili.us",
|
|
7
7
|
"name": "Bas Milius",
|
|
8
8
|
"url": "https://bas.dev"
|
|
9
9
|
},
|
|
10
|
+
"homepage": "https://sparkle.graphics",
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
12
13
|
"url": "https://github.com/basmilius/sparkle.git"
|
|
@@ -44,11 +45,14 @@
|
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@types/node": "^25.5.0",
|
|
48
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
47
49
|
"tsdown": "^0.21.4",
|
|
48
50
|
"typescript": "^6.0.2",
|
|
49
51
|
"vite": "^8.0.3",
|
|
50
52
|
"vitepress": "^1.6.3",
|
|
51
53
|
"vitepress-plugin-example": "^1.4.0",
|
|
52
|
-
"vitepress-plugin-render": "^1.4.0"
|
|
54
|
+
"vitepress-plugin-render": "^1.4.0",
|
|
55
|
+
"vue": "^3.5.13",
|
|
56
|
+
"vue-router": "^4.5.0"
|
|
53
57
|
}
|
|
54
58
|
}
|
package/src/aurora/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Aurora } from './layer';
|
|
2
|
+
import type { AuroraConfig } from './layer';
|
|
3
|
+
import type { Effect } from '../effect';
|
|
4
|
+
|
|
5
|
+
export function createAurora(config?: AuroraConfig): Effect<AuroraConfig> {
|
|
6
|
+
return new Aurora(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { AuroraConfig };
|
|
4
10
|
export type { AuroraBand } from './types';
|
package/src/aurora/layer.ts
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import { hexToRGB } from '@basmilius/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { Effect } from '../effect';
|
|
3
3
|
import { MULBERRY } from './consts';
|
|
4
|
-
import type { AuroraSimulationConfig } from './simulation';
|
|
5
4
|
import type { AuroraBand } from './types';
|
|
6
5
|
|
|
7
6
|
const DEFAULT_COLORS = ['#9922ff', '#4455ff', '#0077ee', '#00aabb', '#22ddff'];
|
|
8
7
|
const TOP_HUE = 265;
|
|
9
8
|
|
|
10
|
-
export
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
|
|
9
|
+
export interface AuroraConfig {
|
|
10
|
+
readonly bands?: number;
|
|
11
|
+
readonly colors?: string[];
|
|
12
|
+
readonly speed?: number;
|
|
13
|
+
readonly intensity?: number;
|
|
14
|
+
readonly waveAmplitude?: number;
|
|
15
|
+
readonly verticalPosition?: number;
|
|
16
|
+
readonly scale?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class Aurora extends Effect<AuroraConfig> {
|
|
20
|
+
#speed: number;
|
|
21
|
+
#intensity: number;
|
|
22
|
+
#waveAmplitude: number;
|
|
23
|
+
#verticalPosition: number;
|
|
16
24
|
#bands: AuroraBand[] = [];
|
|
17
25
|
|
|
18
|
-
constructor(config:
|
|
26
|
+
constructor(config: AuroraConfig = {}) {
|
|
19
27
|
super();
|
|
20
28
|
|
|
21
29
|
const bandCount = config.bands ?? 5;
|
|
@@ -36,26 +44,39 @@ export class AuroraLayer extends SimulationLayer {
|
|
|
36
44
|
|
|
37
45
|
this.#bands.push({
|
|
38
46
|
x: cluster + (MULBERRY.next() - 0.5) * 0.22,
|
|
39
|
-
baseY:
|
|
47
|
+
baseY: (MULBERRY.next() - 0.5) * 0.08,
|
|
40
48
|
height: 0.5 + MULBERRY.next() * 0.3,
|
|
41
49
|
sigma: 160 + MULBERRY.next() * 110,
|
|
42
50
|
phase1: MULBERRY.next() * Math.PI * 2,
|
|
43
51
|
phase2: MULBERRY.next() * Math.PI * 2,
|
|
44
52
|
amplitude1: 0.015 + MULBERRY.next() * 0.025,
|
|
45
53
|
frequency1: 0.003 + MULBERRY.next() * 0.004,
|
|
46
|
-
speed:
|
|
54
|
+
speed: 0.4 + MULBERRY.next() * 0.6,
|
|
47
55
|
hue,
|
|
48
|
-
opacity:
|
|
56
|
+
opacity: 0.5 + MULBERRY.next() * 0.3
|
|
49
57
|
});
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
configure(config: Partial<AuroraConfig>): void {
|
|
62
|
+
if (config.speed !== undefined) {
|
|
63
|
+
this.#speed = config.speed;
|
|
64
|
+
}
|
|
65
|
+
if (config.intensity !== undefined) {
|
|
66
|
+
this.#intensity = config.intensity;
|
|
67
|
+
}
|
|
68
|
+
if (config.waveAmplitude !== undefined) {
|
|
69
|
+
this.#waveAmplitude = config.waveAmplitude;
|
|
70
|
+
}
|
|
71
|
+
if (config.verticalPosition !== undefined) {
|
|
72
|
+
this.#verticalPosition = config.verticalPosition;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
55
75
|
|
|
76
|
+
tick(dt: number, _width: number, _height: number): void {
|
|
56
77
|
for (const band of this.#bands) {
|
|
57
|
-
band.phase1 += 0.005 * band.speed * dt;
|
|
58
|
-
band.phase2 += 0.008 * band.speed * dt;
|
|
78
|
+
band.phase1 += 0.005 * band.speed * this.#speed * dt;
|
|
79
|
+
band.phase2 += 0.008 * band.speed * this.#speed * dt;
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
82
|
|
|
@@ -77,7 +98,7 @@ export class AuroraLayer extends SimulationLayer {
|
|
|
77
98
|
for (const band of this.#bands) {
|
|
78
99
|
const swayX = band.amplitude1 * width * Math.sin(band.phase1);
|
|
79
100
|
const cx = band.x * width + swayX;
|
|
80
|
-
const baseY = band.baseY * height;
|
|
101
|
+
const baseY = (this.#verticalPosition + band.baseY) * height;
|
|
81
102
|
const rayHeight = band.height * height * (height / 800);
|
|
82
103
|
const sigma = band.sigma * scale;
|
|
83
104
|
const cutoff = sigma * 3.5;
|
|
@@ -88,6 +109,21 @@ export class AuroraLayer extends SimulationLayer {
|
|
|
88
109
|
const xStart = Math.max(0, Math.floor((cx - cutoff) / step) * step);
|
|
89
110
|
const xEnd = Math.min(width, Math.ceil((cx + cutoff) / step) * step);
|
|
90
111
|
|
|
112
|
+
const centreWave = Math.sin(band.frequency1 * cx + band.phase2) * waveRange;
|
|
113
|
+
const centreBase = baseY + centreWave;
|
|
114
|
+
const centreTop = centreBase - rayHeight;
|
|
115
|
+
const centreFadeBottom = centreBase + rayHeight * 0.1;
|
|
116
|
+
|
|
117
|
+
const gradient = ctx.createLinearGradient(0, centreFadeBottom, 0, centreTop);
|
|
118
|
+
gradient.addColorStop(0, `hsla(${band.hue}, 100%, 90%, 0)`);
|
|
119
|
+
gradient.addColorStop(0.04, `hsla(${band.hue}, 100%, 90%, 0.55)`);
|
|
120
|
+
gradient.addColorStop(0.1, `hsla(${band.hue}, 90%, 72%, 1)`);
|
|
121
|
+
gradient.addColorStop(0.32, `hsla(${band.hue}, 85%, 62%, 0.75)`);
|
|
122
|
+
gradient.addColorStop(0.62, `hsla(${midHue}, 80%, 56%, 0.35)`);
|
|
123
|
+
gradient.addColorStop(0.86, `hsla(${TOP_HUE}, 75%, 50%, 0.12)`);
|
|
124
|
+
gradient.addColorStop(1, `hsla(${TOP_HUE}, 70%, 45%, 0)`);
|
|
125
|
+
ctx.fillStyle = gradient;
|
|
126
|
+
|
|
91
127
|
for (let x = xStart; x < xEnd; x += step) {
|
|
92
128
|
const dx = x - cx;
|
|
93
129
|
const alpha = Math.exp(-dx * dx / sigmaSq2);
|
|
@@ -100,20 +136,12 @@ export class AuroraLayer extends SimulationLayer {
|
|
|
100
136
|
const colBase = baseY + waveOffset;
|
|
101
137
|
const colTop = colBase - rayHeight;
|
|
102
138
|
const fadeBottom = colBase + rayHeight * 0.1;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const gradient = ctx.createLinearGradient(0, fadeBottom, 0, colTop);
|
|
106
|
-
gradient.addColorStop(0, `hsla(${band.hue}, 100%, 90%, 0)`);
|
|
107
|
-
gradient.addColorStop(0.04, `hsla(${band.hue}, 100%, 90%, ${eff * 0.55})`);
|
|
108
|
-
gradient.addColorStop(0.1, `hsla(${band.hue}, 90%, 72%, ${eff})`);
|
|
109
|
-
gradient.addColorStop(0.32, `hsla(${band.hue}, 85%, 62%, ${eff * 0.75})`);
|
|
110
|
-
gradient.addColorStop(0.62, `hsla(${midHue}, 80%, 56%, ${eff * 0.35})`);
|
|
111
|
-
gradient.addColorStop(0.86, `hsla(${TOP_HUE}, 75%, 50%, ${eff * 0.12})`);
|
|
112
|
-
gradient.addColorStop(1, `hsla(${TOP_HUE}, 70%, 45%, 0)`);
|
|
113
|
-
|
|
114
|
-
ctx.fillStyle = gradient;
|
|
139
|
+
|
|
140
|
+
ctx.globalAlpha = alpha * band.opacity * this.#intensity;
|
|
115
141
|
ctx.fillRect(x, colTop, step, fadeBottom - colTop + 1);
|
|
116
142
|
}
|
|
143
|
+
|
|
144
|
+
ctx.globalAlpha = 1;
|
|
117
145
|
}
|
|
118
146
|
|
|
119
147
|
ctx.globalCompositeOperation = 'source-over';
|
package/src/balloons/index.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { Balloons } from './layer';
|
|
2
|
+
import type { BalloonsConfig } from './layer';
|
|
3
|
+
import type { Effect } from '../effect';
|
|
4
|
+
|
|
5
|
+
export function createBalloons(config?: BalloonsConfig): Effect<BalloonsConfig> {
|
|
6
|
+
return new Balloons(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
2
9
|
export { BalloonParticle } from './particle';
|
|
3
|
-
export {
|
|
10
|
+
export type { BalloonsConfig };
|
|
4
11
|
export type { BalloonParticleConfig } from './particle';
|
|
5
|
-
export type { BalloonSimulationConfig } from './simulation';
|
|
6
12
|
export type { Balloon } from './types';
|
package/src/balloons/layer.ts
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import { hexToRGB } from '@basmilius/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { Effect } from '../effect';
|
|
3
3
|
import { MULBERRY } from './consts';
|
|
4
|
-
import type { BalloonSimulationConfig } from './simulation';
|
|
5
4
|
import type { Balloon } from './types';
|
|
6
5
|
|
|
7
6
|
const DEFAULT_COLORS = ['#ff4444', '#4488ff', '#44cc44', '#ffcc00', '#ff88cc', '#8844ff'];
|
|
8
7
|
|
|
9
|
-
export
|
|
8
|
+
export interface BalloonsConfig {
|
|
9
|
+
readonly count?: number;
|
|
10
|
+
readonly colors?: string[];
|
|
11
|
+
readonly sizeRange?: [number, number];
|
|
12
|
+
readonly speed?: number;
|
|
13
|
+
readonly driftAmount?: number;
|
|
14
|
+
readonly stringLength?: number;
|
|
15
|
+
readonly scale?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class Balloons extends Effect<BalloonsConfig> {
|
|
10
19
|
readonly #scale: number;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
#speed: number;
|
|
21
|
+
#driftAmount: number;
|
|
22
|
+
#stringLengthMul: number;
|
|
14
23
|
readonly #sizeRange: [number, number];
|
|
15
24
|
readonly #colorRGBs: [number, number, number][];
|
|
16
25
|
#maxCount: number;
|
|
17
26
|
#time: number = 0;
|
|
18
27
|
#balloons: Balloon[] = [];
|
|
19
28
|
|
|
20
|
-
constructor(config:
|
|
29
|
+
constructor(config: BalloonsConfig = {}) {
|
|
21
30
|
super();
|
|
22
31
|
|
|
23
32
|
this.#scale = config.scale ?? 1;
|
|
@@ -39,6 +48,18 @@ export class BalloonLayer extends SimulationLayer {
|
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
|
|
51
|
+
configure(config: Partial<BalloonsConfig>): void {
|
|
52
|
+
if (config.speed !== undefined) {
|
|
53
|
+
this.#speed = config.speed;
|
|
54
|
+
}
|
|
55
|
+
if (config.driftAmount !== undefined) {
|
|
56
|
+
this.#driftAmount = config.driftAmount;
|
|
57
|
+
}
|
|
58
|
+
if (config.stringLength !== undefined) {
|
|
59
|
+
this.#stringLengthMul = config.stringLength;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
tick(dt: number, width: number, height: number): void {
|
|
43
64
|
this.#time += 0.015 * dt * this.#speed;
|
|
44
65
|
|
|
@@ -66,10 +87,10 @@ export class BalloonLayer extends SimulationLayer {
|
|
|
66
87
|
const rx = balloon.radiusX * this.#scale;
|
|
67
88
|
const ry = balloon.radiusY * this.#scale;
|
|
68
89
|
const [r, g, b] = balloon.color;
|
|
90
|
+
const cos = Math.cos(balloon.rotation);
|
|
91
|
+
const sin = Math.sin(balloon.rotation);
|
|
69
92
|
|
|
70
|
-
ctx.
|
|
71
|
-
ctx.translate(px, py);
|
|
72
|
-
ctx.rotate(balloon.rotation);
|
|
93
|
+
ctx.setTransform(cos, sin, -sin, cos, px, py);
|
|
73
94
|
|
|
74
95
|
const gradient = ctx.createRadialGradient(
|
|
75
96
|
-rx * 0.3, -ry * 0.3, rx * 0.1,
|
|
@@ -99,21 +120,31 @@ export class BalloonLayer extends SimulationLayer {
|
|
|
99
120
|
ctx.fill();
|
|
100
121
|
|
|
101
122
|
const stringLen = balloon.stringLength * this.#scale * this.#stringLengthMul;
|
|
102
|
-
const
|
|
123
|
+
const knotBaseY = knotY + 5 * this.#scale;
|
|
124
|
+
const ph = balloon.driftPhase;
|
|
125
|
+
const fr = balloon.driftFreq;
|
|
126
|
+
const swingAmt = 10 * this.#scale * this.#driftAmount;
|
|
127
|
+
|
|
128
|
+
// Each control point lags further behind the balloon's lateral oscillation,
|
|
129
|
+
// so the string trails the direction of movement like a real hanging string.
|
|
130
|
+
const midSwing = Math.sin(this.#time * fr + ph - 0.3) * swingAmt * 0.55;
|
|
131
|
+
const tipSwing = Math.sin(this.#time * fr + ph - 0.8) * swingAmt;
|
|
132
|
+
// Subtle high-frequency flutter at the tip for lightness.
|
|
133
|
+
const flutter = Math.sin(this.#time * fr * 2.5 + ph * 1.4 + 1.8) * 2.5 * this.#scale;
|
|
134
|
+
|
|
103
135
|
ctx.beginPath();
|
|
104
|
-
ctx.moveTo(0,
|
|
105
|
-
ctx.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
knotY + 5 * this.#scale + stringLen
|
|
136
|
+
ctx.moveTo(0, knotBaseY);
|
|
137
|
+
ctx.bezierCurveTo(
|
|
138
|
+
midSwing * 0.35, knotBaseY + stringLen * 0.3,
|
|
139
|
+
midSwing + flutter * 0.5, knotBaseY + stringLen * 0.65,
|
|
140
|
+
tipSwing + flutter, knotBaseY + stringLen
|
|
110
141
|
);
|
|
111
142
|
ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, 0.4)`;
|
|
112
143
|
ctx.lineWidth = 1;
|
|
113
144
|
ctx.stroke();
|
|
114
|
-
|
|
115
|
-
ctx.restore();
|
|
116
145
|
}
|
|
146
|
+
|
|
147
|
+
ctx.resetTransform();
|
|
117
148
|
}
|
|
118
149
|
|
|
119
150
|
#createBalloon(initialSpread: boolean): Balloon {
|
package/src/bubbles/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Bubbles } from './layer';
|
|
2
|
+
import type { BubblesConfig } from './layer';
|
|
3
|
+
import type { Effect } from '../effect';
|
|
4
|
+
|
|
5
|
+
export function createBubbles(config?: BubblesConfig): Effect<BubblesConfig> {
|
|
6
|
+
return new Bubbles(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { BubblesConfig };
|
|
4
10
|
export type { Bubble, PopParticle } from './types';
|
package/src/bubbles/layer.ts
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseColor } from '../color';
|
|
2
|
+
import { Effect } from '../effect';
|
|
2
3
|
import { MULBERRY } from './consts';
|
|
3
|
-
import type { BubbleSimulationConfig } from './simulation';
|
|
4
4
|
import type { Bubble, PopParticle } from './types';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_COLORS = ['#88ccff', '#aaddff', '#ccbbff'];
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export interface BubblesConfig {
|
|
9
|
+
readonly count?: number;
|
|
10
|
+
readonly sizeRange?: [number, number];
|
|
11
|
+
readonly speed?: number;
|
|
12
|
+
readonly popOnClick?: boolean;
|
|
13
|
+
readonly popRadius?: number;
|
|
14
|
+
readonly colors?: string[];
|
|
15
|
+
readonly wobbleAmount?: number;
|
|
16
|
+
readonly scale?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class Bubbles extends Effect<BubblesConfig> {
|
|
9
20
|
readonly #scale: number;
|
|
10
|
-
|
|
21
|
+
#speed: number;
|
|
11
22
|
readonly #sizeRange: [number, number];
|
|
12
|
-
|
|
23
|
+
#wobbleAmount: number;
|
|
13
24
|
readonly #popOnClick: boolean;
|
|
14
25
|
readonly #popRadius: number;
|
|
15
26
|
readonly #baseHues: number[];
|
|
@@ -20,7 +31,7 @@ export class BubbleLayer extends SimulationLayer {
|
|
|
20
31
|
#popParticles: PopParticle[] = [];
|
|
21
32
|
#canvas: HTMLCanvasElement | null = null;
|
|
22
33
|
|
|
23
|
-
constructor(config:
|
|
34
|
+
constructor(config: BubblesConfig = {}) {
|
|
24
35
|
super();
|
|
25
36
|
|
|
26
37
|
this.#scale = config.scale ?? 1;
|
|
@@ -58,6 +69,15 @@ export class BubbleLayer extends SimulationLayer {
|
|
|
58
69
|
this.#canvas = null;
|
|
59
70
|
}
|
|
60
71
|
|
|
72
|
+
configure(config: Partial<BubblesConfig>): void {
|
|
73
|
+
if (config.speed !== undefined) {
|
|
74
|
+
this.#speed = config.speed;
|
|
75
|
+
}
|
|
76
|
+
if (config.wobbleAmount !== undefined) {
|
|
77
|
+
this.#wobbleAmount = config.wobbleAmount;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
61
81
|
tick(dt: number, width: number, height: number): void {
|
|
62
82
|
this.#time += 0.01 * dt;
|
|
63
83
|
|
|
@@ -193,17 +213,10 @@ export class BubbleLayer extends SimulationLayer {
|
|
|
193
213
|
}
|
|
194
214
|
|
|
195
215
|
#colorToHue(color: string): number {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
ctx.fillStyle = color;
|
|
201
|
-
ctx.fillRect(0, 0, 1, 1);
|
|
202
|
-
const data = ctx.getImageData(0, 0, 1, 1).data;
|
|
203
|
-
|
|
204
|
-
let r = data[0] / 255;
|
|
205
|
-
let g = data[1] / 255;
|
|
206
|
-
let b = data[2] / 255;
|
|
216
|
+
const {r: r255, g: g255, b: b255} = parseColor(color);
|
|
217
|
+
let r = r255 / 255;
|
|
218
|
+
let g = g255 / 255;
|
|
219
|
+
let b = b255 / 255;
|
|
207
220
|
const max = Math.max(r, g, b);
|
|
208
221
|
const min = Math.min(r, g, b);
|
|
209
222
|
const delta = max - min;
|
package/src/canvas.ts
CHANGED
|
@@ -120,6 +120,18 @@ export class LimitedFrameRateCanvas {
|
|
|
120
120
|
cancelAnimationFrame(this.#frame);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
pause(): void {
|
|
124
|
+
this.#isStopped = true;
|
|
125
|
+
cancelAnimationFrame(this.#frame);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
resume(): void {
|
|
129
|
+
if (this.#isStopped) {
|
|
130
|
+
this.#isStopped = false;
|
|
131
|
+
this.#frame = requestAnimationFrame(this.loop.bind(this));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
123
135
|
draw(): void {
|
|
124
136
|
throw new Error('LimitedFrameRateCanvas::draw() should be overwritten.');
|
|
125
137
|
}
|
package/src/color.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
const cache = new Map<string, { r: number; g: number; b: number; a: number }>();
|
|
2
|
+
|
|
3
|
+
export function parseColor(fillStyle: string): { r: number; g: number; b: number; a: number } {
|
|
4
|
+
const cached = cache.get(fillStyle);
|
|
5
|
+
if (cached) {
|
|
6
|
+
return cached;
|
|
7
|
+
}
|
|
8
|
+
|
|
2
9
|
const canvas = document.createElement('canvas');
|
|
3
10
|
canvas.width = 1;
|
|
4
11
|
canvas.height = 1;
|
|
@@ -6,5 +13,7 @@ export function parseColor(fillStyle: string): {r: number; g: number; b: number;
|
|
|
6
13
|
ctx.fillStyle = fillStyle;
|
|
7
14
|
ctx.fillRect(0, 0, 1, 1);
|
|
8
15
|
const data = ctx.getImageData(0, 0, 1, 1).data;
|
|
9
|
-
|
|
16
|
+
const result = {r: data[0], g: data[1], b: data[2], a: data[3] / 255};
|
|
17
|
+
cache.set(fillStyle, result);
|
|
18
|
+
return result;
|
|
10
19
|
}
|
package/src/confetti/index.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { Confetti } from './layer';
|
|
2
|
+
import type { ConfettiConfig } from './layer';
|
|
3
|
+
import type { Config as ConfettiBurstConfig } from './types';
|
|
4
|
+
import type { Effect } from '../effect';
|
|
5
|
+
|
|
6
|
+
export interface ConfettiInstance extends Effect<ConfettiConfig> {
|
|
7
|
+
burst(config: Partial<ConfettiBurstConfig>): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createConfetti(config?: ConfettiConfig): ConfettiInstance {
|
|
11
|
+
return new Confetti(config) as ConfettiInstance;
|
|
12
|
+
}
|
|
13
|
+
|
|
2
14
|
export { ConfettiParticle } from './particle';
|
|
3
|
-
export { ConfettiSimulation } from './simulation';
|
|
4
15
|
export { PALETTES } from './consts';
|
|
5
16
|
export { SHAPE_PATHS } from './shapes';
|
|
17
|
+
export type { ConfettiConfig };
|
|
18
|
+
export type { ConfettiBurstConfig };
|
|
6
19
|
export type { ConfettiParticleConfig } from './particle';
|
|
7
|
-
export type { ConfettiSimulationConfig } from './simulation';
|
|
8
20
|
export type { Palette, Shape as ConfettiShape } from './types';
|
package/src/confetti/layer.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { hexToRGB } from '@basmilius/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { Effect } from '../effect';
|
|
3
3
|
import { DEFAULT_CONFIG, MULBERRY, PALETTES } from './consts';
|
|
4
4
|
import { SHAPE_PATHS } from './shapes';
|
|
5
|
-
import type { ConfettiSimulationConfig } from './simulation';
|
|
6
5
|
import type { Config, Particle, ParticleConfig } from './types';
|
|
7
6
|
|
|
8
7
|
const TWO_PI = Math.PI * 2;
|
|
9
8
|
|
|
10
|
-
export
|
|
9
|
+
export interface ConfettiConfig {
|
|
10
|
+
readonly scale?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Confetti extends Effect<ConfettiConfig> {
|
|
11
14
|
readonly #scale: number;
|
|
12
15
|
#particles: Particle[] = [];
|
|
13
16
|
#width: number = 0;
|
|
14
17
|
#height: number = 0;
|
|
15
18
|
#isFiring: boolean = false;
|
|
16
19
|
|
|
17
|
-
constructor(config:
|
|
20
|
+
constructor(config: ConfettiConfig = {}) {
|
|
18
21
|
super();
|
|
19
22
|
this.#scale = config.scale ?? 1;
|
|
20
23
|
}
|
|
@@ -24,7 +27,7 @@ export class ConfettiLayer extends SimulationLayer {
|
|
|
24
27
|
this.#height = height;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
burst(config: Partial<Config>): void {
|
|
28
31
|
const width = this.#width;
|
|
29
32
|
const height = this.#height;
|
|
30
33
|
|
package/src/confetti/particle.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Point } from '../point';
|
|
2
|
+
import { MULBERRY } from './consts';
|
|
2
3
|
import { SHAPE_PATHS } from './shapes';
|
|
3
4
|
import type { Shape } from './types';
|
|
4
5
|
|
|
@@ -47,29 +48,29 @@ export class ConfettiParticle {
|
|
|
47
48
|
const startVelocity = (config.startVelocity ?? 45) * scale;
|
|
48
49
|
const launchAngle = -(direction * Math.PI / 180)
|
|
49
50
|
+ (0.5 * spread * Math.PI / 180)
|
|
50
|
-
- (
|
|
51
|
-
const speed = startVelocity * (0.5 +
|
|
52
|
-
const rotAngle =
|
|
51
|
+
- (MULBERRY.next() * spread * Math.PI / 180);
|
|
52
|
+
const speed = startVelocity * (0.5 + MULBERRY.next());
|
|
53
|
+
const rotAngle = MULBERRY.next() * Math.PI * 2;
|
|
53
54
|
|
|
54
55
|
this.#colorStr = color;
|
|
55
56
|
this.#gravity = (config.gravity ?? 1) * scale;
|
|
56
57
|
this.#shape = shape;
|
|
57
|
-
this.#size = (5 +
|
|
58
|
+
this.#size = (5 + MULBERRY.next() * 5) * scale;
|
|
58
59
|
this.#totalTicks = config.ticks ?? 200;
|
|
59
60
|
this.#x = position.x;
|
|
60
61
|
this.#y = position.y;
|
|
61
62
|
this.#vx = Math.cos(launchAngle) * speed;
|
|
62
63
|
this.#vy = Math.sin(launchAngle) * speed;
|
|
63
|
-
this.#decay = (config.decay ?? 0.9) - 0.05 +
|
|
64
|
-
this.#flipAngle =
|
|
65
|
-
this.#flipSpeed = 0.03 +
|
|
64
|
+
this.#decay = (config.decay ?? 0.9) - 0.05 + MULBERRY.next() * 0.1;
|
|
65
|
+
this.#flipAngle = MULBERRY.next() * Math.PI * 2;
|
|
66
|
+
this.#flipSpeed = 0.03 + MULBERRY.next() * 0.05;
|
|
66
67
|
this.#rotAngle = rotAngle;
|
|
67
68
|
this.#rotCos = Math.cos(rotAngle);
|
|
68
69
|
this.#rotSin = Math.sin(rotAngle);
|
|
69
|
-
this.#rotSpeed = (
|
|
70
|
-
this.#swing =
|
|
71
|
-
this.#swingAmp = 0.5 +
|
|
72
|
-
this.#swingSpeed = 0.025 +
|
|
70
|
+
this.#rotSpeed = (MULBERRY.next() - 0.5) * 0.06;
|
|
71
|
+
this.#swing = MULBERRY.next() * Math.PI * 2;
|
|
72
|
+
this.#swingAmp = 0.5 + MULBERRY.next() * 1.5;
|
|
73
|
+
this.#swingSpeed = 0.025 + MULBERRY.next() * 0.035;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
draw(ctx: CanvasRenderingContext2D): void {
|
package/src/donuts/consts.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type Mulberry32, mulberry32 } from '@basmilius/utils';
|
|
2
|
-
import type {
|
|
2
|
+
import type { DonutsConfig } from './layer';
|
|
3
3
|
|
|
4
4
|
export const MULBERRY: Mulberry32 = mulberry32(13);
|
|
5
5
|
|
|
6
|
-
export const DEFAULT_CONFIG:
|
|
6
|
+
export const DEFAULT_CONFIG: DonutsConfig = {
|
|
7
7
|
background: '#a51955',
|
|
8
8
|
collisionPadding: 20,
|
|
9
9
|
colors: ['#bd1961', '#da287c'],
|
package/src/donuts/index.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Donuts } from './layer';
|
|
2
|
+
import type { DonutsConfig } from './layer';
|
|
3
|
+
import type { Effect } from '../effect';
|
|
4
|
+
|
|
5
|
+
export function createDonuts(config?: DonutsConfig): Effect<DonutsConfig> {
|
|
6
|
+
return new Donuts(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { DonutsConfig };
|