@konfetti-js/core 0.3.0 → 0.4.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/README.md +1 -1
- package/dist/index.cjs +188 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +188 -56
- package/dist/index.js.map +1 -1
- package/dist/konfetti.min.js +1 -1
- package/dist/konfetti.min.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45,7 +45,7 @@ var DEFAULT_OPTIONS = {
|
|
|
45
45
|
decay: 0.9,
|
|
46
46
|
gravity: 1,
|
|
47
47
|
drift: 0,
|
|
48
|
-
ticks:
|
|
48
|
+
ticks: 600,
|
|
49
49
|
origin: { x: 0.5, y: 0.5 },
|
|
50
50
|
colors: DEFAULT_COLORS,
|
|
51
51
|
shapes: ["circle", "square"],
|
|
@@ -103,6 +103,9 @@ function degToRad(deg) {
|
|
|
103
103
|
function randomRange(min, max) {
|
|
104
104
|
return Math.random() * (max - min) + min;
|
|
105
105
|
}
|
|
106
|
+
function randomSign() {
|
|
107
|
+
return Math.random() < 0.5 ? -1 : 1;
|
|
108
|
+
}
|
|
106
109
|
function randomItem(arr) {
|
|
107
110
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
108
111
|
}
|
|
@@ -131,11 +134,11 @@ var ParticlePool = class {
|
|
|
131
134
|
*/
|
|
132
135
|
this.palette = [];
|
|
133
136
|
this.colorIds = /* @__PURE__ */ new Map();
|
|
134
|
-
this.data = new Float32Array(initialCapacity *
|
|
137
|
+
this.data = new Float32Array(initialCapacity * 25 /* SIZE */);
|
|
135
138
|
}
|
|
136
139
|
/** How many particles fit before the buffer has to grow */
|
|
137
140
|
get capacity() {
|
|
138
|
-
return this.data.length /
|
|
141
|
+
return this.data.length / 25 /* SIZE */;
|
|
139
142
|
}
|
|
140
143
|
/**
|
|
141
144
|
* Remove every particle
|
|
@@ -157,17 +160,19 @@ var ParticlePool = class {
|
|
|
157
160
|
const angleRad = degToRad(options.angle);
|
|
158
161
|
const spreadRad = degToRad(options.spread);
|
|
159
162
|
for (let i = 0; i < count; i++) {
|
|
160
|
-
const idx = (this.activeCount + i) *
|
|
163
|
+
const idx = (this.activeCount + i) * 25 /* SIZE */;
|
|
161
164
|
const particleAngle = angleRad + randomRange(-spreadRad / 2, spreadRad / 2);
|
|
162
165
|
const velocity = options.startVelocity * randomRange(0.5, 1);
|
|
163
166
|
data[idx + 0 /* X */] = originX;
|
|
164
167
|
data[idx + 1 /* Y */] = originY;
|
|
165
168
|
data[idx + 2 /* VelocityX */] = Math.cos(particleAngle) * velocity;
|
|
166
169
|
data[idx + 3 /* VelocityY */] = -Math.sin(particleAngle) * velocity;
|
|
167
|
-
data[idx + 4 /* Rotation */] = randomRange(0, Math.PI * 2);
|
|
168
|
-
data[idx + 5 /* RotationSpeed */] = randomRange(-0.1, 0.1);
|
|
169
170
|
data[idx + 6 /* Tilt */] = randomRange(0, Math.PI * 2);
|
|
170
|
-
data[idx + 7 /* TiltSpeed */] = randomRange(0.
|
|
171
|
+
data[idx + 7 /* TiltSpeed */] = randomRange(0.08, 0.22) * randomSign();
|
|
172
|
+
data[idx + 16 /* Wobble */] = randomRange(0, Math.PI * 2);
|
|
173
|
+
data[idx + 17 /* WobbleSpeed */] = randomRange(0.02, 0.07) * randomSign();
|
|
174
|
+
data[idx + 4 /* Rotation */] = randomRange(0, Math.PI * 2);
|
|
175
|
+
data[idx + 5 /* RotationSpeed */] = randomRange(-0.04, 0.04);
|
|
171
176
|
data[idx + 8 /* Color */] = this.colorId(randomItem(options.colors));
|
|
172
177
|
data[idx + 9 /* Shape */] = shapeToIndex(randomItem(options.shapes));
|
|
173
178
|
data[idx + 10 /* Scalar */] = options.scalar * randomRange(0.8, 1.2);
|
|
@@ -176,6 +181,8 @@ var ParticlePool = class {
|
|
|
176
181
|
data[idx + 13 /* Drift */] = options.drift + randomRange(-0.5, 0.5);
|
|
177
182
|
data[idx + 14 /* Gravity */] = options.gravity;
|
|
178
183
|
data[idx + 15 /* Decay */] = options.decay;
|
|
184
|
+
data[idx + 18 /* AirPhase */] = randomRange(0, Math.PI * 2);
|
|
185
|
+
data[idx + 19 /* AirPhaseSpeed */] = randomRange(0.02, 0.05);
|
|
179
186
|
}
|
|
180
187
|
this.activeCount += count;
|
|
181
188
|
}
|
|
@@ -186,11 +193,11 @@ var ParticlePool = class {
|
|
|
186
193
|
const data = this.data;
|
|
187
194
|
let writeIdx = 0;
|
|
188
195
|
for (let readIdx = 0; readIdx < this.activeCount; readIdx++) {
|
|
189
|
-
const srcIdx = readIdx *
|
|
196
|
+
const srcIdx = readIdx * 25 /* SIZE */;
|
|
190
197
|
if (data[srcIdx + 11 /* Life */] <= 0) continue;
|
|
191
198
|
if (writeIdx !== readIdx) {
|
|
192
|
-
const dstIdx = writeIdx *
|
|
193
|
-
data.copyWithin(dstIdx, srcIdx, srcIdx +
|
|
199
|
+
const dstIdx = writeIdx * 25 /* SIZE */;
|
|
200
|
+
data.copyWithin(dstIdx, srcIdx, srcIdx + 25 /* SIZE */);
|
|
194
201
|
}
|
|
195
202
|
writeIdx++;
|
|
196
203
|
}
|
|
@@ -211,7 +218,7 @@ var ParticlePool = class {
|
|
|
211
218
|
const remap = /* @__PURE__ */ new Map();
|
|
212
219
|
this.clearPalette();
|
|
213
220
|
for (let i = 0; i < this.activeCount; i++) {
|
|
214
|
-
const idx = i *
|
|
221
|
+
const idx = i * 25 /* SIZE */ + 8 /* Color */;
|
|
215
222
|
const oldId = data[idx];
|
|
216
223
|
let newId = remap.get(oldId);
|
|
217
224
|
if (newId === void 0) {
|
|
@@ -238,36 +245,105 @@ var ParticlePool = class {
|
|
|
238
245
|
if (needed <= this.capacity) return;
|
|
239
246
|
let capacity = this.capacity;
|
|
240
247
|
while (capacity < needed) capacity *= 2;
|
|
241
|
-
const grown = new Float32Array(Math.min(capacity, MAX_PARTICLES) *
|
|
242
|
-
grown.set(this.data.subarray(0, this.activeCount *
|
|
248
|
+
const grown = new Float32Array(Math.min(capacity, MAX_PARTICLES) * 25 /* SIZE */);
|
|
249
|
+
grown.set(this.data.subarray(0, this.activeCount * 25 /* SIZE */));
|
|
243
250
|
this.data = grown;
|
|
244
251
|
}
|
|
245
252
|
};
|
|
246
253
|
|
|
254
|
+
// src/orientation.ts
|
|
255
|
+
var LIGHT_X = -0.3;
|
|
256
|
+
var LIGHT_Y = -0.6;
|
|
257
|
+
var LIGHT_Z = 0.742;
|
|
258
|
+
function lightness(m) {
|
|
259
|
+
const facing = m[8] < 0 ? -1 : 1;
|
|
260
|
+
return Math.max(0, facing * (m[6] * LIGHT_X + m[7] * LIGHT_Y + m[8] * LIGHT_Z));
|
|
261
|
+
}
|
|
262
|
+
function createOrientation() {
|
|
263
|
+
return new Float64Array(9);
|
|
264
|
+
}
|
|
265
|
+
function orient(data, idx, out) {
|
|
266
|
+
const tilt = data[idx + 6 /* Tilt */];
|
|
267
|
+
const wobble = data[idx + 16 /* Wobble */];
|
|
268
|
+
const rotation = data[idx + 4 /* Rotation */];
|
|
269
|
+
const cx = Math.cos(tilt);
|
|
270
|
+
const sx = Math.sin(tilt);
|
|
271
|
+
const cy = Math.cos(wobble);
|
|
272
|
+
const sy = Math.sin(wobble);
|
|
273
|
+
const cz = Math.cos(rotation);
|
|
274
|
+
const sz = Math.sin(rotation);
|
|
275
|
+
out[0] = cz * cy;
|
|
276
|
+
out[1] = sz * cy;
|
|
277
|
+
out[2] = -sy;
|
|
278
|
+
out[3] = cz * sy * sx - sz * cx;
|
|
279
|
+
out[4] = sz * sy * sx + cz * cx;
|
|
280
|
+
out[5] = cy * sx;
|
|
281
|
+
out[6] = cz * sy * cx + sz * sx;
|
|
282
|
+
out[7] = sz * sy * cx - cz * sx;
|
|
283
|
+
out[8] = cy * cx;
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
|
|
247
287
|
// src/physics.ts
|
|
248
|
-
|
|
288
|
+
var PAPER_GRAVITY = 0.45;
|
|
289
|
+
var FALL_DRAG = 0.0475;
|
|
290
|
+
var FLUTTER_SPEED = 4;
|
|
291
|
+
var GLIDE = 0.6;
|
|
292
|
+
var AIR_SWAY = 1;
|
|
293
|
+
var OFFSCREEN_MARGIN = 60;
|
|
294
|
+
var orientation = createOrientation();
|
|
295
|
+
function updateParticles(pool, dt, width, height) {
|
|
249
296
|
const data = pool.data;
|
|
250
297
|
for (let i = 0; i < pool.activeCount; i++) {
|
|
251
|
-
|
|
252
|
-
let vx = data[idx + 2 /* VelocityX */];
|
|
253
|
-
let vy = data[idx + 3 /* VelocityY */];
|
|
254
|
-
const tilt = data[idx + 6 /* Tilt */];
|
|
255
|
-
vy += data[idx + 14 /* Gravity */] * dt;
|
|
256
|
-
vx += data[idx + 13 /* Drift */] * Math.sin(tilt) * dt;
|
|
257
|
-
const damping = data[idx + 15 /* Decay */] ** dt;
|
|
258
|
-
vx *= damping;
|
|
259
|
-
vy *= damping;
|
|
260
|
-
data[idx + 2 /* VelocityX */] = vx;
|
|
261
|
-
data[idx + 3 /* VelocityY */] = vy;
|
|
262
|
-
data[idx + 0 /* X */] += vx * dt;
|
|
263
|
-
data[idx + 1 /* Y */] += vy * dt;
|
|
264
|
-
data[idx + 4 /* Rotation */] += data[idx + 5 /* RotationSpeed */] * dt;
|
|
265
|
-
data[idx + 6 /* Tilt */] = tilt + data[idx + 7 /* TiltSpeed */] * dt;
|
|
266
|
-
data[idx + 11 /* Life */] -= dt;
|
|
298
|
+
updateParticle(data, i * 25 /* SIZE */, dt, width, height);
|
|
267
299
|
}
|
|
268
300
|
pool.compact();
|
|
269
301
|
return pool.activeCount > 0;
|
|
270
302
|
}
|
|
303
|
+
function updateParticle(data, idx, dt, width, height) {
|
|
304
|
+
let vx = data[idx + 2 /* VelocityX */];
|
|
305
|
+
let vy = data[idx + 3 /* VelocityY */];
|
|
306
|
+
const m = orient(data, idx, orientation);
|
|
307
|
+
const nx = m[6];
|
|
308
|
+
const ny = m[7];
|
|
309
|
+
data[idx + 20 /* AxisXX */] = m[0];
|
|
310
|
+
data[idx + 21 /* AxisXY */] = m[1];
|
|
311
|
+
data[idx + 22 /* AxisYX */] = m[3];
|
|
312
|
+
data[idx + 23 /* AxisYY */] = m[4];
|
|
313
|
+
data[idx + 24 /* Light */] = lightness(m);
|
|
314
|
+
vy += data[idx + 14 /* Gravity */] * PAPER_GRAVITY * dt;
|
|
315
|
+
if (vy > 0) {
|
|
316
|
+
const drag = FALL_DRAG * (0.35 + 0.65 * Math.abs(ny));
|
|
317
|
+
vy = Math.max(0, vy - drag * vy * Math.min(vy, FLUTTER_SPEED) * dt);
|
|
318
|
+
vx -= GLIDE * vy * 2 * nx * ny * dt;
|
|
319
|
+
}
|
|
320
|
+
const phase = data[idx + 18 /* AirPhase */];
|
|
321
|
+
const airX = data[idx + 13 /* Drift */] + AIR_SWAY * Math.sin(phase);
|
|
322
|
+
const damping = data[idx + 15 /* Decay */] ** dt;
|
|
323
|
+
vx = airX + (vx - airX) * damping;
|
|
324
|
+
vy *= damping;
|
|
325
|
+
const spin = 0.6 + Math.min(1.4, Math.hypot(vx, vy) / 3);
|
|
326
|
+
data[idx + 2 /* VelocityX */] = vx;
|
|
327
|
+
data[idx + 3 /* VelocityY */] = vy;
|
|
328
|
+
data[idx + 0 /* X */] += vx * dt;
|
|
329
|
+
data[idx + 1 /* Y */] += vy * dt;
|
|
330
|
+
data[idx + 6 /* Tilt */] += data[idx + 7 /* TiltSpeed */] * spin * dt;
|
|
331
|
+
data[idx + 16 /* Wobble */] += data[idx + 17 /* WobbleSpeed */] * dt;
|
|
332
|
+
data[idx + 4 /* Rotation */] += data[idx + 5 /* RotationSpeed */] * dt;
|
|
333
|
+
data[idx + 18 /* AirPhase */] = phase + data[idx + 19 /* AirPhaseSpeed */] * dt;
|
|
334
|
+
data[idx + 11 /* Life */] -= dt;
|
|
335
|
+
if (width > 0 && height > 0) {
|
|
336
|
+
const x = data[idx + 0 /* X */];
|
|
337
|
+
const y = data[idx + 1 /* Y */];
|
|
338
|
+
const fellOut = y > height + OFFSCREEN_MARGIN && vy >= 0;
|
|
339
|
+
const leftOut = x < -OFFSCREEN_MARGIN * 2 && vx <= 0;
|
|
340
|
+
const rightOut = x > width + OFFSCREEN_MARGIN * 2 && vx >= 0;
|
|
341
|
+
const floatedOut = y < -OFFSCREEN_MARGIN && vy <= 0 && data[idx + 14 /* Gravity */] <= 0;
|
|
342
|
+
if (fellOut || leftOut || rightOut || floatedOut) {
|
|
343
|
+
data[idx + 11 /* Life */] = 0;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
271
347
|
|
|
272
348
|
// src/presets.ts
|
|
273
349
|
var FIREWORK_COLORS = ["#ff0000", "#ffff00", "#00ff00", "#00ffff", "#ff00ff"];
|
|
@@ -352,7 +428,7 @@ var PRESETS = {
|
|
|
352
428
|
gravity: 0.5,
|
|
353
429
|
drift: 0,
|
|
354
430
|
origin: { x: 0.5, y: 0 },
|
|
355
|
-
ticks:
|
|
431
|
+
ticks: 900
|
|
356
432
|
}
|
|
357
433
|
}
|
|
358
434
|
]
|
|
@@ -371,7 +447,7 @@ var PRESETS = {
|
|
|
371
447
|
gravity: 0.3,
|
|
372
448
|
drift: 1,
|
|
373
449
|
origin: { x: 0.5, y: -0.1 },
|
|
374
|
-
ticks:
|
|
450
|
+
ticks: 1200,
|
|
375
451
|
colors: ["#ffffff", "#e0e0e0", "#c0c0c0"],
|
|
376
452
|
shapes: ["circle"]
|
|
377
453
|
}
|
|
@@ -431,6 +507,40 @@ function isPresetName(value) {
|
|
|
431
507
|
|
|
432
508
|
// src/renderer.ts
|
|
433
509
|
var BASE_SIZE = 10;
|
|
510
|
+
var SHADE_STEPS = 16;
|
|
511
|
+
var MIN_BRIGHTNESS = 0.55;
|
|
512
|
+
var MAX_BRIGHTNESS = 1.2;
|
|
513
|
+
var LIFETIME_FADE = 0.1;
|
|
514
|
+
var MAX_SHADE_CACHE = 4096;
|
|
515
|
+
var BLACK = [0, 0, 0, 1];
|
|
516
|
+
function parseColor(ctx, color) {
|
|
517
|
+
ctx.fillStyle = "#000000";
|
|
518
|
+
ctx.fillStyle = color;
|
|
519
|
+
const overBlack = String(ctx.fillStyle);
|
|
520
|
+
ctx.fillStyle = "#ffffff";
|
|
521
|
+
ctx.fillStyle = color;
|
|
522
|
+
if (String(ctx.fillStyle) !== overBlack) return null;
|
|
523
|
+
const hex = /^#([0-9a-f]{6})$/i.exec(overBlack);
|
|
524
|
+
if (hex) {
|
|
525
|
+
const value = Number.parseInt(hex[1], 16);
|
|
526
|
+
return [value >> 16, value >> 8 & 255, value & 255, 1];
|
|
527
|
+
}
|
|
528
|
+
const rgba = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?/.exec(overBlack);
|
|
529
|
+
if (!rgba) return null;
|
|
530
|
+
const alpha = rgba[4] === void 0 ? 1 : Number(rgba[4]);
|
|
531
|
+
return [Number(rgba[1]), Number(rgba[2]), Number(rgba[3]), alpha];
|
|
532
|
+
}
|
|
533
|
+
function buildShades([r, g, b, alpha]) {
|
|
534
|
+
const shades = [];
|
|
535
|
+
for (let step = 0; step < SHADE_STEPS; step++) {
|
|
536
|
+
const brightness = MIN_BRIGHTNESS + (MAX_BRIGHTNESS - MIN_BRIGHTNESS) * step / (SHADE_STEPS - 1);
|
|
537
|
+
const [sr, sg, sb] = [r, g, b].map(
|
|
538
|
+
(c) => Math.round(brightness <= 1 ? c * brightness : c + (255 - c) * (brightness - 1))
|
|
539
|
+
);
|
|
540
|
+
shades.push(alpha < 1 ? `rgba(${sr}, ${sg}, ${sb}, ${alpha})` : `rgb(${sr}, ${sg}, ${sb})`);
|
|
541
|
+
}
|
|
542
|
+
return shades;
|
|
543
|
+
}
|
|
434
544
|
function createOverlayCanvas() {
|
|
435
545
|
const canvas = document.createElement("canvas");
|
|
436
546
|
canvas.className = "konfetti-canvas";
|
|
@@ -453,7 +563,9 @@ var Renderer = class {
|
|
|
453
563
|
this.width = 0;
|
|
454
564
|
this.height = 0;
|
|
455
565
|
this.pixelRatio = 1;
|
|
456
|
-
/**
|
|
566
|
+
/** Brightness steps per color */
|
|
567
|
+
this.shadeCache = /* @__PURE__ */ new Map();
|
|
568
|
+
/** Particle indices grouped by color and shade step, reused between frames */
|
|
457
569
|
this.buckets = [];
|
|
458
570
|
this.ownsCanvas = !canvas;
|
|
459
571
|
this.canvas = canvas ?? createOverlayCanvas();
|
|
@@ -501,7 +613,10 @@ var Renderer = class {
|
|
|
501
613
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
502
614
|
}
|
|
503
615
|
/**
|
|
504
|
-
* Draw
|
|
616
|
+
* Draw every piece as flat paper turned in 3D: the transform is the piece's
|
|
617
|
+
* rotation projected onto the screen (computed by physics), and its color is
|
|
618
|
+
* shaded by how much its visible side faces the light. Pieces are drawn
|
|
619
|
+
* grouped by color and shade, so the fill changes once per group.
|
|
505
620
|
*/
|
|
506
621
|
render(pool) {
|
|
507
622
|
this.clear();
|
|
@@ -510,35 +625,32 @@ var Renderer = class {
|
|
|
510
625
|
const data = pool.data;
|
|
511
626
|
const ratio = this.pixelRatio;
|
|
512
627
|
const buckets = this.buckets;
|
|
513
|
-
for (const bucket of buckets) bucket.length = 0;
|
|
628
|
+
for (const bucket of buckets) if (bucket) bucket.length = 0;
|
|
514
629
|
for (let i = 0; i < pool.activeCount; i++) {
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
630
|
+
const idx = i * 25 /* SIZE */;
|
|
631
|
+
const step = Math.round(data[idx + 24 /* Light */] * (SHADE_STEPS - 1));
|
|
632
|
+
const key = data[idx + 8 /* Color */] * SHADE_STEPS + step;
|
|
633
|
+
if (!buckets[key]) buckets[key] = [];
|
|
634
|
+
buckets[key].push(idx);
|
|
518
635
|
}
|
|
519
|
-
for (let
|
|
520
|
-
const bucket = buckets[
|
|
636
|
+
for (let key = 0; key < buckets.length; key++) {
|
|
637
|
+
const bucket = buckets[key];
|
|
521
638
|
if (!bucket || bucket.length === 0) continue;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const tilt = data[idx + 6 /* Tilt */];
|
|
526
|
-
const rotation = data[idx + 4 /* Rotation */];
|
|
639
|
+
const color = pool.palette[Math.floor(key / SHADE_STEPS)];
|
|
640
|
+
ctx.fillStyle = this.shadesFor(ctx, color)[key % SHADE_STEPS];
|
|
641
|
+
for (const idx of bucket) {
|
|
527
642
|
const opacity = Math.min(
|
|
528
643
|
1,
|
|
529
|
-
data[idx + 11 /* Life */] / data[idx + 12 /* MaxLife */] *
|
|
644
|
+
data[idx + 11 /* Life */] / (data[idx + 12 /* MaxLife */] * LIFETIME_FADE)
|
|
530
645
|
);
|
|
531
|
-
const size = BASE_SIZE * data[idx + 10 /* Scalar */]
|
|
646
|
+
const size = BASE_SIZE * data[idx + 10 /* Scalar */];
|
|
532
647
|
if (size < 0.5 || opacity < 0.01) continue;
|
|
533
|
-
const cos = Math.cos(rotation);
|
|
534
|
-
const sin = Math.sin(rotation);
|
|
535
|
-
const squash = 0.6 + 0.4 * Math.abs(Math.cos(tilt));
|
|
536
648
|
ctx.globalAlpha = opacity;
|
|
537
649
|
ctx.setTransform(
|
|
538
|
-
ratio *
|
|
539
|
-
ratio *
|
|
540
|
-
|
|
541
|
-
ratio *
|
|
650
|
+
ratio * data[idx + 20 /* AxisXX */],
|
|
651
|
+
ratio * data[idx + 21 /* AxisXY */],
|
|
652
|
+
ratio * data[idx + 22 /* AxisYX */],
|
|
653
|
+
ratio * data[idx + 23 /* AxisYY */],
|
|
542
654
|
ratio * data[idx + 0 /* X */],
|
|
543
655
|
ratio * data[idx + 1 /* Y */]
|
|
544
656
|
);
|
|
@@ -548,6 +660,15 @@ var Renderer = class {
|
|
|
548
660
|
ctx.globalAlpha = 1;
|
|
549
661
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
550
662
|
}
|
|
663
|
+
shadesFor(ctx, color) {
|
|
664
|
+
let shades = this.shadeCache.get(color);
|
|
665
|
+
if (!shades) {
|
|
666
|
+
if (this.shadeCache.size >= MAX_SHADE_CACHE) this.shadeCache.clear();
|
|
667
|
+
shades = buildShades(parseColor(ctx, color) ?? BLACK);
|
|
668
|
+
this.shadeCache.set(color, shades);
|
|
669
|
+
}
|
|
670
|
+
return shades;
|
|
671
|
+
}
|
|
551
672
|
/**
|
|
552
673
|
* Destroy renderer and remove the canvas if we created it
|
|
553
674
|
*/
|
|
@@ -570,6 +691,8 @@ var Konfetti = class {
|
|
|
570
691
|
this.paused = false;
|
|
571
692
|
this.destroyed = false;
|
|
572
693
|
this.resizeWithWindow = false;
|
|
694
|
+
/** Follows a custom canvas whose size changes through layout, not just window resizes */
|
|
695
|
+
this.resizeObserver = null;
|
|
573
696
|
this.timeouts = /* @__PURE__ */ new Set();
|
|
574
697
|
this.intervals = /* @__PURE__ */ new Set();
|
|
575
698
|
this.handleResize = () => {
|
|
@@ -587,7 +710,8 @@ var Konfetti = class {
|
|
|
587
710
|
if (!this.renderer) return;
|
|
588
711
|
const dt = clamp((time - this.lastTime) / FRAME_MS, 0, MAX_STEP);
|
|
589
712
|
this.lastTime = time;
|
|
590
|
-
const
|
|
713
|
+
const { width, height } = this.renderer.getDimensions();
|
|
714
|
+
const hasParticles = updateParticles(this.pool, dt, width, height);
|
|
591
715
|
this.renderer.render(this.pool);
|
|
592
716
|
if (hasParticles) {
|
|
593
717
|
this.frameId = requestAnimationFrame(this.tick);
|
|
@@ -599,7 +723,13 @@ var Konfetti = class {
|
|
|
599
723
|
}
|
|
600
724
|
this.renderer = new Renderer(canvas);
|
|
601
725
|
this.resizeWithWindow = createOptions.resize !== false;
|
|
602
|
-
if (this.resizeWithWindow)
|
|
726
|
+
if (this.resizeWithWindow) {
|
|
727
|
+
window.addEventListener("resize", this.handleResize);
|
|
728
|
+
if (canvas && typeof ResizeObserver !== "undefined") {
|
|
729
|
+
this.resizeObserver = new ResizeObserver(this.handleResize);
|
|
730
|
+
this.resizeObserver.observe(canvas);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
603
733
|
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
604
734
|
}
|
|
605
735
|
/**
|
|
@@ -686,6 +816,8 @@ var Konfetti = class {
|
|
|
686
816
|
this.reset();
|
|
687
817
|
this.destroyed = true;
|
|
688
818
|
if (this.resizeWithWindow) window.removeEventListener("resize", this.handleResize);
|
|
819
|
+
this.resizeObserver?.disconnect();
|
|
820
|
+
this.resizeObserver = null;
|
|
689
821
|
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
690
822
|
this.renderer?.destroy();
|
|
691
823
|
}
|