@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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @konfetti-js/core
|
|
2
2
|
|
|
3
|
-
A lightweight, zero-dependency confetti animation library for the web. About
|
|
3
|
+
A lightweight, zero-dependency confetti animation library for the web. Pieces behave like paper: they tumble in 3D, catch the light and float down. About 5 kB gzipped, TypeScript-first, and safe to import during server rendering.
|
|
4
4
|
|
|
5
5
|
**[Live demo and docs](https://andreabettich.github.io/konfetti.js/)**
|
|
6
6
|
|
package/dist/index.cjs
CHANGED
|
@@ -88,7 +88,7 @@ var DEFAULT_OPTIONS = {
|
|
|
88
88
|
decay: 0.9,
|
|
89
89
|
gravity: 1,
|
|
90
90
|
drift: 0,
|
|
91
|
-
ticks:
|
|
91
|
+
ticks: 600,
|
|
92
92
|
origin: { x: 0.5, y: 0.5 },
|
|
93
93
|
colors: DEFAULT_COLORS,
|
|
94
94
|
shapes: ["circle", "square"],
|
|
@@ -146,6 +146,9 @@ function degToRad(deg) {
|
|
|
146
146
|
function randomRange(min, max) {
|
|
147
147
|
return Math.random() * (max - min) + min;
|
|
148
148
|
}
|
|
149
|
+
function randomSign() {
|
|
150
|
+
return Math.random() < 0.5 ? -1 : 1;
|
|
151
|
+
}
|
|
149
152
|
function randomItem(arr) {
|
|
150
153
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
151
154
|
}
|
|
@@ -174,11 +177,11 @@ var ParticlePool = class {
|
|
|
174
177
|
*/
|
|
175
178
|
this.palette = [];
|
|
176
179
|
this.colorIds = /* @__PURE__ */ new Map();
|
|
177
|
-
this.data = new Float32Array(initialCapacity *
|
|
180
|
+
this.data = new Float32Array(initialCapacity * 25 /* SIZE */);
|
|
178
181
|
}
|
|
179
182
|
/** How many particles fit before the buffer has to grow */
|
|
180
183
|
get capacity() {
|
|
181
|
-
return this.data.length /
|
|
184
|
+
return this.data.length / 25 /* SIZE */;
|
|
182
185
|
}
|
|
183
186
|
/**
|
|
184
187
|
* Remove every particle
|
|
@@ -200,17 +203,19 @@ var ParticlePool = class {
|
|
|
200
203
|
const angleRad = degToRad(options.angle);
|
|
201
204
|
const spreadRad = degToRad(options.spread);
|
|
202
205
|
for (let i = 0; i < count; i++) {
|
|
203
|
-
const idx = (this.activeCount + i) *
|
|
206
|
+
const idx = (this.activeCount + i) * 25 /* SIZE */;
|
|
204
207
|
const particleAngle = angleRad + randomRange(-spreadRad / 2, spreadRad / 2);
|
|
205
208
|
const velocity = options.startVelocity * randomRange(0.5, 1);
|
|
206
209
|
data[idx + 0 /* X */] = originX;
|
|
207
210
|
data[idx + 1 /* Y */] = originY;
|
|
208
211
|
data[idx + 2 /* VelocityX */] = Math.cos(particleAngle) * velocity;
|
|
209
212
|
data[idx + 3 /* VelocityY */] = -Math.sin(particleAngle) * velocity;
|
|
210
|
-
data[idx + 4 /* Rotation */] = randomRange(0, Math.PI * 2);
|
|
211
|
-
data[idx + 5 /* RotationSpeed */] = randomRange(-0.1, 0.1);
|
|
212
213
|
data[idx + 6 /* Tilt */] = randomRange(0, Math.PI * 2);
|
|
213
|
-
data[idx + 7 /* TiltSpeed */] = randomRange(0.
|
|
214
|
+
data[idx + 7 /* TiltSpeed */] = randomRange(0.08, 0.22) * randomSign();
|
|
215
|
+
data[idx + 16 /* Wobble */] = randomRange(0, Math.PI * 2);
|
|
216
|
+
data[idx + 17 /* WobbleSpeed */] = randomRange(0.02, 0.07) * randomSign();
|
|
217
|
+
data[idx + 4 /* Rotation */] = randomRange(0, Math.PI * 2);
|
|
218
|
+
data[idx + 5 /* RotationSpeed */] = randomRange(-0.04, 0.04);
|
|
214
219
|
data[idx + 8 /* Color */] = this.colorId(randomItem(options.colors));
|
|
215
220
|
data[idx + 9 /* Shape */] = shapeToIndex(randomItem(options.shapes));
|
|
216
221
|
data[idx + 10 /* Scalar */] = options.scalar * randomRange(0.8, 1.2);
|
|
@@ -219,6 +224,8 @@ var ParticlePool = class {
|
|
|
219
224
|
data[idx + 13 /* Drift */] = options.drift + randomRange(-0.5, 0.5);
|
|
220
225
|
data[idx + 14 /* Gravity */] = options.gravity;
|
|
221
226
|
data[idx + 15 /* Decay */] = options.decay;
|
|
227
|
+
data[idx + 18 /* AirPhase */] = randomRange(0, Math.PI * 2);
|
|
228
|
+
data[idx + 19 /* AirPhaseSpeed */] = randomRange(0.02, 0.05);
|
|
222
229
|
}
|
|
223
230
|
this.activeCount += count;
|
|
224
231
|
}
|
|
@@ -229,11 +236,11 @@ var ParticlePool = class {
|
|
|
229
236
|
const data = this.data;
|
|
230
237
|
let writeIdx = 0;
|
|
231
238
|
for (let readIdx = 0; readIdx < this.activeCount; readIdx++) {
|
|
232
|
-
const srcIdx = readIdx *
|
|
239
|
+
const srcIdx = readIdx * 25 /* SIZE */;
|
|
233
240
|
if (data[srcIdx + 11 /* Life */] <= 0) continue;
|
|
234
241
|
if (writeIdx !== readIdx) {
|
|
235
|
-
const dstIdx = writeIdx *
|
|
236
|
-
data.copyWithin(dstIdx, srcIdx, srcIdx +
|
|
242
|
+
const dstIdx = writeIdx * 25 /* SIZE */;
|
|
243
|
+
data.copyWithin(dstIdx, srcIdx, srcIdx + 25 /* SIZE */);
|
|
237
244
|
}
|
|
238
245
|
writeIdx++;
|
|
239
246
|
}
|
|
@@ -254,7 +261,7 @@ var ParticlePool = class {
|
|
|
254
261
|
const remap = /* @__PURE__ */ new Map();
|
|
255
262
|
this.clearPalette();
|
|
256
263
|
for (let i = 0; i < this.activeCount; i++) {
|
|
257
|
-
const idx = i *
|
|
264
|
+
const idx = i * 25 /* SIZE */ + 8 /* Color */;
|
|
258
265
|
const oldId = data[idx];
|
|
259
266
|
let newId = remap.get(oldId);
|
|
260
267
|
if (newId === void 0) {
|
|
@@ -281,36 +288,105 @@ var ParticlePool = class {
|
|
|
281
288
|
if (needed <= this.capacity) return;
|
|
282
289
|
let capacity = this.capacity;
|
|
283
290
|
while (capacity < needed) capacity *= 2;
|
|
284
|
-
const grown = new Float32Array(Math.min(capacity, MAX_PARTICLES) *
|
|
285
|
-
grown.set(this.data.subarray(0, this.activeCount *
|
|
291
|
+
const grown = new Float32Array(Math.min(capacity, MAX_PARTICLES) * 25 /* SIZE */);
|
|
292
|
+
grown.set(this.data.subarray(0, this.activeCount * 25 /* SIZE */));
|
|
286
293
|
this.data = grown;
|
|
287
294
|
}
|
|
288
295
|
};
|
|
289
296
|
|
|
297
|
+
// src/orientation.ts
|
|
298
|
+
var LIGHT_X = -0.3;
|
|
299
|
+
var LIGHT_Y = -0.6;
|
|
300
|
+
var LIGHT_Z = 0.742;
|
|
301
|
+
function lightness(m) {
|
|
302
|
+
const facing = m[8] < 0 ? -1 : 1;
|
|
303
|
+
return Math.max(0, facing * (m[6] * LIGHT_X + m[7] * LIGHT_Y + m[8] * LIGHT_Z));
|
|
304
|
+
}
|
|
305
|
+
function createOrientation() {
|
|
306
|
+
return new Float64Array(9);
|
|
307
|
+
}
|
|
308
|
+
function orient(data, idx, out) {
|
|
309
|
+
const tilt = data[idx + 6 /* Tilt */];
|
|
310
|
+
const wobble = data[idx + 16 /* Wobble */];
|
|
311
|
+
const rotation = data[idx + 4 /* Rotation */];
|
|
312
|
+
const cx = Math.cos(tilt);
|
|
313
|
+
const sx = Math.sin(tilt);
|
|
314
|
+
const cy = Math.cos(wobble);
|
|
315
|
+
const sy = Math.sin(wobble);
|
|
316
|
+
const cz = Math.cos(rotation);
|
|
317
|
+
const sz = Math.sin(rotation);
|
|
318
|
+
out[0] = cz * cy;
|
|
319
|
+
out[1] = sz * cy;
|
|
320
|
+
out[2] = -sy;
|
|
321
|
+
out[3] = cz * sy * sx - sz * cx;
|
|
322
|
+
out[4] = sz * sy * sx + cz * cx;
|
|
323
|
+
out[5] = cy * sx;
|
|
324
|
+
out[6] = cz * sy * cx + sz * sx;
|
|
325
|
+
out[7] = sz * sy * cx - cz * sx;
|
|
326
|
+
out[8] = cy * cx;
|
|
327
|
+
return out;
|
|
328
|
+
}
|
|
329
|
+
|
|
290
330
|
// src/physics.ts
|
|
291
|
-
|
|
331
|
+
var PAPER_GRAVITY = 0.45;
|
|
332
|
+
var FALL_DRAG = 0.0475;
|
|
333
|
+
var FLUTTER_SPEED = 4;
|
|
334
|
+
var GLIDE = 0.6;
|
|
335
|
+
var AIR_SWAY = 1;
|
|
336
|
+
var OFFSCREEN_MARGIN = 60;
|
|
337
|
+
var orientation = createOrientation();
|
|
338
|
+
function updateParticles(pool, dt, width, height) {
|
|
292
339
|
const data = pool.data;
|
|
293
340
|
for (let i = 0; i < pool.activeCount; i++) {
|
|
294
|
-
|
|
295
|
-
let vx = data[idx + 2 /* VelocityX */];
|
|
296
|
-
let vy = data[idx + 3 /* VelocityY */];
|
|
297
|
-
const tilt = data[idx + 6 /* Tilt */];
|
|
298
|
-
vy += data[idx + 14 /* Gravity */] * dt;
|
|
299
|
-
vx += data[idx + 13 /* Drift */] * Math.sin(tilt) * dt;
|
|
300
|
-
const damping = data[idx + 15 /* Decay */] ** dt;
|
|
301
|
-
vx *= damping;
|
|
302
|
-
vy *= damping;
|
|
303
|
-
data[idx + 2 /* VelocityX */] = vx;
|
|
304
|
-
data[idx + 3 /* VelocityY */] = vy;
|
|
305
|
-
data[idx + 0 /* X */] += vx * dt;
|
|
306
|
-
data[idx + 1 /* Y */] += vy * dt;
|
|
307
|
-
data[idx + 4 /* Rotation */] += data[idx + 5 /* RotationSpeed */] * dt;
|
|
308
|
-
data[idx + 6 /* Tilt */] = tilt + data[idx + 7 /* TiltSpeed */] * dt;
|
|
309
|
-
data[idx + 11 /* Life */] -= dt;
|
|
341
|
+
updateParticle(data, i * 25 /* SIZE */, dt, width, height);
|
|
310
342
|
}
|
|
311
343
|
pool.compact();
|
|
312
344
|
return pool.activeCount > 0;
|
|
313
345
|
}
|
|
346
|
+
function updateParticle(data, idx, dt, width, height) {
|
|
347
|
+
let vx = data[idx + 2 /* VelocityX */];
|
|
348
|
+
let vy = data[idx + 3 /* VelocityY */];
|
|
349
|
+
const m = orient(data, idx, orientation);
|
|
350
|
+
const nx = m[6];
|
|
351
|
+
const ny = m[7];
|
|
352
|
+
data[idx + 20 /* AxisXX */] = m[0];
|
|
353
|
+
data[idx + 21 /* AxisXY */] = m[1];
|
|
354
|
+
data[idx + 22 /* AxisYX */] = m[3];
|
|
355
|
+
data[idx + 23 /* AxisYY */] = m[4];
|
|
356
|
+
data[idx + 24 /* Light */] = lightness(m);
|
|
357
|
+
vy += data[idx + 14 /* Gravity */] * PAPER_GRAVITY * dt;
|
|
358
|
+
if (vy > 0) {
|
|
359
|
+
const drag = FALL_DRAG * (0.35 + 0.65 * Math.abs(ny));
|
|
360
|
+
vy = Math.max(0, vy - drag * vy * Math.min(vy, FLUTTER_SPEED) * dt);
|
|
361
|
+
vx -= GLIDE * vy * 2 * nx * ny * dt;
|
|
362
|
+
}
|
|
363
|
+
const phase = data[idx + 18 /* AirPhase */];
|
|
364
|
+
const airX = data[idx + 13 /* Drift */] + AIR_SWAY * Math.sin(phase);
|
|
365
|
+
const damping = data[idx + 15 /* Decay */] ** dt;
|
|
366
|
+
vx = airX + (vx - airX) * damping;
|
|
367
|
+
vy *= damping;
|
|
368
|
+
const spin = 0.6 + Math.min(1.4, Math.hypot(vx, vy) / 3);
|
|
369
|
+
data[idx + 2 /* VelocityX */] = vx;
|
|
370
|
+
data[idx + 3 /* VelocityY */] = vy;
|
|
371
|
+
data[idx + 0 /* X */] += vx * dt;
|
|
372
|
+
data[idx + 1 /* Y */] += vy * dt;
|
|
373
|
+
data[idx + 6 /* Tilt */] += data[idx + 7 /* TiltSpeed */] * spin * dt;
|
|
374
|
+
data[idx + 16 /* Wobble */] += data[idx + 17 /* WobbleSpeed */] * dt;
|
|
375
|
+
data[idx + 4 /* Rotation */] += data[idx + 5 /* RotationSpeed */] * dt;
|
|
376
|
+
data[idx + 18 /* AirPhase */] = phase + data[idx + 19 /* AirPhaseSpeed */] * dt;
|
|
377
|
+
data[idx + 11 /* Life */] -= dt;
|
|
378
|
+
if (width > 0 && height > 0) {
|
|
379
|
+
const x = data[idx + 0 /* X */];
|
|
380
|
+
const y = data[idx + 1 /* Y */];
|
|
381
|
+
const fellOut = y > height + OFFSCREEN_MARGIN && vy >= 0;
|
|
382
|
+
const leftOut = x < -OFFSCREEN_MARGIN * 2 && vx <= 0;
|
|
383
|
+
const rightOut = x > width + OFFSCREEN_MARGIN * 2 && vx >= 0;
|
|
384
|
+
const floatedOut = y < -OFFSCREEN_MARGIN && vy <= 0 && data[idx + 14 /* Gravity */] <= 0;
|
|
385
|
+
if (fellOut || leftOut || rightOut || floatedOut) {
|
|
386
|
+
data[idx + 11 /* Life */] = 0;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
314
390
|
|
|
315
391
|
// src/presets.ts
|
|
316
392
|
var FIREWORK_COLORS = ["#ff0000", "#ffff00", "#00ff00", "#00ffff", "#ff00ff"];
|
|
@@ -395,7 +471,7 @@ var PRESETS = {
|
|
|
395
471
|
gravity: 0.5,
|
|
396
472
|
drift: 0,
|
|
397
473
|
origin: { x: 0.5, y: 0 },
|
|
398
|
-
ticks:
|
|
474
|
+
ticks: 900
|
|
399
475
|
}
|
|
400
476
|
}
|
|
401
477
|
]
|
|
@@ -414,7 +490,7 @@ var PRESETS = {
|
|
|
414
490
|
gravity: 0.3,
|
|
415
491
|
drift: 1,
|
|
416
492
|
origin: { x: 0.5, y: -0.1 },
|
|
417
|
-
ticks:
|
|
493
|
+
ticks: 1200,
|
|
418
494
|
colors: ["#ffffff", "#e0e0e0", "#c0c0c0"],
|
|
419
495
|
shapes: ["circle"]
|
|
420
496
|
}
|
|
@@ -474,6 +550,40 @@ function isPresetName(value) {
|
|
|
474
550
|
|
|
475
551
|
// src/renderer.ts
|
|
476
552
|
var BASE_SIZE = 10;
|
|
553
|
+
var SHADE_STEPS = 16;
|
|
554
|
+
var MIN_BRIGHTNESS = 0.55;
|
|
555
|
+
var MAX_BRIGHTNESS = 1.2;
|
|
556
|
+
var LIFETIME_FADE = 0.1;
|
|
557
|
+
var MAX_SHADE_CACHE = 4096;
|
|
558
|
+
var BLACK = [0, 0, 0, 1];
|
|
559
|
+
function parseColor(ctx, color) {
|
|
560
|
+
ctx.fillStyle = "#000000";
|
|
561
|
+
ctx.fillStyle = color;
|
|
562
|
+
const overBlack = String(ctx.fillStyle);
|
|
563
|
+
ctx.fillStyle = "#ffffff";
|
|
564
|
+
ctx.fillStyle = color;
|
|
565
|
+
if (String(ctx.fillStyle) !== overBlack) return null;
|
|
566
|
+
const hex = /^#([0-9a-f]{6})$/i.exec(overBlack);
|
|
567
|
+
if (hex) {
|
|
568
|
+
const value = Number.parseInt(hex[1], 16);
|
|
569
|
+
return [value >> 16, value >> 8 & 255, value & 255, 1];
|
|
570
|
+
}
|
|
571
|
+
const rgba = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?/.exec(overBlack);
|
|
572
|
+
if (!rgba) return null;
|
|
573
|
+
const alpha = rgba[4] === void 0 ? 1 : Number(rgba[4]);
|
|
574
|
+
return [Number(rgba[1]), Number(rgba[2]), Number(rgba[3]), alpha];
|
|
575
|
+
}
|
|
576
|
+
function buildShades([r, g, b, alpha]) {
|
|
577
|
+
const shades = [];
|
|
578
|
+
for (let step = 0; step < SHADE_STEPS; step++) {
|
|
579
|
+
const brightness = MIN_BRIGHTNESS + (MAX_BRIGHTNESS - MIN_BRIGHTNESS) * step / (SHADE_STEPS - 1);
|
|
580
|
+
const [sr, sg, sb] = [r, g, b].map(
|
|
581
|
+
(c) => Math.round(brightness <= 1 ? c * brightness : c + (255 - c) * (brightness - 1))
|
|
582
|
+
);
|
|
583
|
+
shades.push(alpha < 1 ? `rgba(${sr}, ${sg}, ${sb}, ${alpha})` : `rgb(${sr}, ${sg}, ${sb})`);
|
|
584
|
+
}
|
|
585
|
+
return shades;
|
|
586
|
+
}
|
|
477
587
|
function createOverlayCanvas() {
|
|
478
588
|
const canvas = document.createElement("canvas");
|
|
479
589
|
canvas.className = "konfetti-canvas";
|
|
@@ -496,7 +606,9 @@ var Renderer = class {
|
|
|
496
606
|
this.width = 0;
|
|
497
607
|
this.height = 0;
|
|
498
608
|
this.pixelRatio = 1;
|
|
499
|
-
/**
|
|
609
|
+
/** Brightness steps per color */
|
|
610
|
+
this.shadeCache = /* @__PURE__ */ new Map();
|
|
611
|
+
/** Particle indices grouped by color and shade step, reused between frames */
|
|
500
612
|
this.buckets = [];
|
|
501
613
|
this.ownsCanvas = !canvas;
|
|
502
614
|
this.canvas = canvas ?? createOverlayCanvas();
|
|
@@ -544,7 +656,10 @@ var Renderer = class {
|
|
|
544
656
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
545
657
|
}
|
|
546
658
|
/**
|
|
547
|
-
* Draw
|
|
659
|
+
* Draw every piece as flat paper turned in 3D: the transform is the piece's
|
|
660
|
+
* rotation projected onto the screen (computed by physics), and its color is
|
|
661
|
+
* shaded by how much its visible side faces the light. Pieces are drawn
|
|
662
|
+
* grouped by color and shade, so the fill changes once per group.
|
|
548
663
|
*/
|
|
549
664
|
render(pool) {
|
|
550
665
|
this.clear();
|
|
@@ -553,35 +668,32 @@ var Renderer = class {
|
|
|
553
668
|
const data = pool.data;
|
|
554
669
|
const ratio = this.pixelRatio;
|
|
555
670
|
const buckets = this.buckets;
|
|
556
|
-
for (const bucket of buckets) bucket.length = 0;
|
|
671
|
+
for (const bucket of buckets) if (bucket) bucket.length = 0;
|
|
557
672
|
for (let i = 0; i < pool.activeCount; i++) {
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
673
|
+
const idx = i * 25 /* SIZE */;
|
|
674
|
+
const step = Math.round(data[idx + 24 /* Light */] * (SHADE_STEPS - 1));
|
|
675
|
+
const key = data[idx + 8 /* Color */] * SHADE_STEPS + step;
|
|
676
|
+
if (!buckets[key]) buckets[key] = [];
|
|
677
|
+
buckets[key].push(idx);
|
|
561
678
|
}
|
|
562
|
-
for (let
|
|
563
|
-
const bucket = buckets[
|
|
679
|
+
for (let key = 0; key < buckets.length; key++) {
|
|
680
|
+
const bucket = buckets[key];
|
|
564
681
|
if (!bucket || bucket.length === 0) continue;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const tilt = data[idx + 6 /* Tilt */];
|
|
569
|
-
const rotation = data[idx + 4 /* Rotation */];
|
|
682
|
+
const color = pool.palette[Math.floor(key / SHADE_STEPS)];
|
|
683
|
+
ctx.fillStyle = this.shadesFor(ctx, color)[key % SHADE_STEPS];
|
|
684
|
+
for (const idx of bucket) {
|
|
570
685
|
const opacity = Math.min(
|
|
571
686
|
1,
|
|
572
|
-
data[idx + 11 /* Life */] / data[idx + 12 /* MaxLife */] *
|
|
687
|
+
data[idx + 11 /* Life */] / (data[idx + 12 /* MaxLife */] * LIFETIME_FADE)
|
|
573
688
|
);
|
|
574
|
-
const size = BASE_SIZE * data[idx + 10 /* Scalar */]
|
|
689
|
+
const size = BASE_SIZE * data[idx + 10 /* Scalar */];
|
|
575
690
|
if (size < 0.5 || opacity < 0.01) continue;
|
|
576
|
-
const cos = Math.cos(rotation);
|
|
577
|
-
const sin = Math.sin(rotation);
|
|
578
|
-
const squash = 0.6 + 0.4 * Math.abs(Math.cos(tilt));
|
|
579
691
|
ctx.globalAlpha = opacity;
|
|
580
692
|
ctx.setTransform(
|
|
581
|
-
ratio *
|
|
582
|
-
ratio *
|
|
583
|
-
|
|
584
|
-
ratio *
|
|
693
|
+
ratio * data[idx + 20 /* AxisXX */],
|
|
694
|
+
ratio * data[idx + 21 /* AxisXY */],
|
|
695
|
+
ratio * data[idx + 22 /* AxisYX */],
|
|
696
|
+
ratio * data[idx + 23 /* AxisYY */],
|
|
585
697
|
ratio * data[idx + 0 /* X */],
|
|
586
698
|
ratio * data[idx + 1 /* Y */]
|
|
587
699
|
);
|
|
@@ -591,6 +703,15 @@ var Renderer = class {
|
|
|
591
703
|
ctx.globalAlpha = 1;
|
|
592
704
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
593
705
|
}
|
|
706
|
+
shadesFor(ctx, color) {
|
|
707
|
+
let shades = this.shadeCache.get(color);
|
|
708
|
+
if (!shades) {
|
|
709
|
+
if (this.shadeCache.size >= MAX_SHADE_CACHE) this.shadeCache.clear();
|
|
710
|
+
shades = buildShades(parseColor(ctx, color) ?? BLACK);
|
|
711
|
+
this.shadeCache.set(color, shades);
|
|
712
|
+
}
|
|
713
|
+
return shades;
|
|
714
|
+
}
|
|
594
715
|
/**
|
|
595
716
|
* Destroy renderer and remove the canvas if we created it
|
|
596
717
|
*/
|
|
@@ -613,6 +734,8 @@ var Konfetti = class {
|
|
|
613
734
|
this.paused = false;
|
|
614
735
|
this.destroyed = false;
|
|
615
736
|
this.resizeWithWindow = false;
|
|
737
|
+
/** Follows a custom canvas whose size changes through layout, not just window resizes */
|
|
738
|
+
this.resizeObserver = null;
|
|
616
739
|
this.timeouts = /* @__PURE__ */ new Set();
|
|
617
740
|
this.intervals = /* @__PURE__ */ new Set();
|
|
618
741
|
this.handleResize = () => {
|
|
@@ -630,7 +753,8 @@ var Konfetti = class {
|
|
|
630
753
|
if (!this.renderer) return;
|
|
631
754
|
const dt = clamp((time - this.lastTime) / FRAME_MS, 0, MAX_STEP);
|
|
632
755
|
this.lastTime = time;
|
|
633
|
-
const
|
|
756
|
+
const { width, height } = this.renderer.getDimensions();
|
|
757
|
+
const hasParticles = updateParticles(this.pool, dt, width, height);
|
|
634
758
|
this.renderer.render(this.pool);
|
|
635
759
|
if (hasParticles) {
|
|
636
760
|
this.frameId = requestAnimationFrame(this.tick);
|
|
@@ -642,7 +766,13 @@ var Konfetti = class {
|
|
|
642
766
|
}
|
|
643
767
|
this.renderer = new Renderer(canvas);
|
|
644
768
|
this.resizeWithWindow = createOptions.resize !== false;
|
|
645
|
-
if (this.resizeWithWindow)
|
|
769
|
+
if (this.resizeWithWindow) {
|
|
770
|
+
window.addEventListener("resize", this.handleResize);
|
|
771
|
+
if (canvas && typeof ResizeObserver !== "undefined") {
|
|
772
|
+
this.resizeObserver = new ResizeObserver(this.handleResize);
|
|
773
|
+
this.resizeObserver.observe(canvas);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
646
776
|
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
647
777
|
}
|
|
648
778
|
/**
|
|
@@ -729,6 +859,8 @@ var Konfetti = class {
|
|
|
729
859
|
this.reset();
|
|
730
860
|
this.destroyed = true;
|
|
731
861
|
if (this.resizeWithWindow) window.removeEventListener("resize", this.handleResize);
|
|
862
|
+
this.resizeObserver?.disconnect();
|
|
863
|
+
this.resizeObserver = null;
|
|
732
864
|
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
733
865
|
this.renderer?.destroy();
|
|
734
866
|
}
|