@mccustomapps/helaui 0.1.10 → 0.1.12
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 +3 -3
- package/dist/index.cjs +45 -5
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +45 -5
- package/dist/rain-background.css +8 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -216,7 +216,7 @@ import '@mccustomapps/helaui/rain-background.css';
|
|
|
216
216
|
const rain = new RainBackground();
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
-
**Next.js (App Router)** — use a Client Component. `RainBackground` already mounts itself on `document.body`; do not
|
|
219
|
+
**Next.js (App Router)** — use a Client Component. `RainBackground` already mounts itself on `document.body`; do **not** call `insertBefore` / `appendChild` on `rain.element` a second time.
|
|
220
220
|
|
|
221
221
|
```tsx
|
|
222
222
|
'use client';
|
|
@@ -227,7 +227,7 @@ import '@mccustomapps/helaui/rain-background.css';
|
|
|
227
227
|
|
|
228
228
|
export function HelaRain() {
|
|
229
229
|
useEffect(() => {
|
|
230
|
-
const rain = new RainBackground();
|
|
230
|
+
const rain = new RainBackground({ zIndex: 0 });
|
|
231
231
|
return () => rain.destroy();
|
|
232
232
|
}, []);
|
|
233
233
|
|
|
@@ -241,7 +241,7 @@ export function HelaRain() {
|
|
|
241
241
|
|---|---|---|---|
|
|
242
242
|
| `images` | `string[]` | built-in artwork | Image URLs |
|
|
243
243
|
| `density` | `number` | `28` | Number of particles |
|
|
244
|
-
| `minSize` / `maxSize` | `number` | `28` | Particle width in pixels |
|
|
244
|
+
| `minSize` / `maxSize` | `number` | `28` / `72` | Particle width in pixels |
|
|
245
245
|
| `minDuration` / `maxDuration` | `number` | `8` / `18` | Fall duration in seconds |
|
|
246
246
|
| `target` | `HTMLElement` | `document.body` | Mount container |
|
|
247
247
|
| `zIndex` | `number` | `0` | Stacking order |
|
package/dist/index.cjs
CHANGED
|
@@ -405,16 +405,43 @@ function pick(items) {
|
|
|
405
405
|
return items[Math.floor(Math.random() * items.length)];
|
|
406
406
|
}
|
|
407
407
|
function toImageUrl(source) {
|
|
408
|
-
|
|
408
|
+
if (typeof source === "string") return source;
|
|
409
|
+
if (source && typeof source === "object") {
|
|
410
|
+
const value = source;
|
|
411
|
+
if (typeof value.src === "string") return value.src;
|
|
412
|
+
if (typeof value.default === "string") return value.default;
|
|
413
|
+
if (value.default && typeof value.default === "object" && value.default !== null) {
|
|
414
|
+
const nested = value.default;
|
|
415
|
+
if (typeof nested.src === "string") return nested.src;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
throw new Error("RainBackground image must be a URL string.");
|
|
419
|
+
}
|
|
420
|
+
function toDisplayUrl(source, cache) {
|
|
421
|
+
const existing = cache.get(source);
|
|
422
|
+
if (existing) return existing;
|
|
423
|
+
if (!source.startsWith("data:")) return source;
|
|
424
|
+
const comma = source.indexOf(",");
|
|
425
|
+
if (comma === -1) return source;
|
|
426
|
+
const header = source.slice(0, comma);
|
|
427
|
+
const data = source.slice(comma + 1);
|
|
428
|
+
const mime = header.match(/^data:([^;,]+)/i)?.[1] ?? "image/png";
|
|
429
|
+
const binary = atob(data);
|
|
430
|
+
const bytes = new Uint8Array(binary.length);
|
|
431
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
432
|
+
const url = URL.createObjectURL(new Blob([bytes], { type: mime }));
|
|
433
|
+
cache.set(source, url);
|
|
434
|
+
return url;
|
|
409
435
|
}
|
|
410
436
|
var RainBackground = class {
|
|
411
437
|
element;
|
|
438
|
+
objectUrls = [];
|
|
412
439
|
constructor(options = {}) {
|
|
413
440
|
const {
|
|
414
441
|
images = [...BUILTIN_RAIN_IMAGES],
|
|
415
442
|
density = 28,
|
|
416
443
|
minSize = 28,
|
|
417
|
-
maxSize =
|
|
444
|
+
maxSize = 72,
|
|
418
445
|
minDuration = 8,
|
|
419
446
|
maxDuration = 18,
|
|
420
447
|
target = document.body,
|
|
@@ -427,8 +454,11 @@ var RainBackground = class {
|
|
|
427
454
|
this.element.className = "helaui-rain-background";
|
|
428
455
|
this.element.setAttribute("aria-hidden", "true");
|
|
429
456
|
this.element.style.zIndex = String(zIndex);
|
|
457
|
+
const urlCache = /* @__PURE__ */ new Map();
|
|
458
|
+
const resolved = images.map((image) => toDisplayUrl(toImageUrl(image), urlCache));
|
|
459
|
+
this.objectUrls.push(...new Set(urlCache.values()));
|
|
430
460
|
for (let index = 0; index < density; index += 1) {
|
|
431
|
-
this.element.appendChild(this.createDrop(
|
|
461
|
+
this.element.appendChild(this.createDrop(resolved, minSize, maxSize, minDuration, maxDuration));
|
|
432
462
|
}
|
|
433
463
|
target.prepend(this.element);
|
|
434
464
|
}
|
|
@@ -436,9 +466,16 @@ var RainBackground = class {
|
|
|
436
466
|
const drop = document.createElement("div");
|
|
437
467
|
const duration = randomBetween(minDuration, maxDuration);
|
|
438
468
|
const size = randomBetween(minSize, maxSize);
|
|
439
|
-
const src =
|
|
469
|
+
const src = pick(images);
|
|
470
|
+
const img = document.createElement("img");
|
|
471
|
+
img.className = "helaui-rain-background__image";
|
|
472
|
+
img.src = src;
|
|
473
|
+
img.alt = "";
|
|
474
|
+
img.draggable = false;
|
|
475
|
+
img.decoding = "async";
|
|
476
|
+
img.loading = "eager";
|
|
477
|
+
img.setAttribute("fetchpriority", "high");
|
|
440
478
|
drop.className = "helaui-rain-background__drop";
|
|
441
|
-
drop.style.backgroundImage = `url(${JSON.stringify(src)})`;
|
|
442
479
|
drop.style.setProperty("--helaui-rain-x", `${randomBetween(0, 100)}%`);
|
|
443
480
|
drop.style.setProperty("--helaui-rain-size", `${size}px`);
|
|
444
481
|
drop.style.setProperty("--helaui-rain-duration", `${duration.toFixed(2)}s`);
|
|
@@ -446,11 +483,14 @@ var RainBackground = class {
|
|
|
446
483
|
drop.style.setProperty("--helaui-rain-rotation", `${randomBetween(-35, 35).toFixed(1)}deg`);
|
|
447
484
|
drop.style.setProperty("--helaui-rain-spin", `${randomBetween(90, 360).toFixed(0)}deg`);
|
|
448
485
|
drop.style.setProperty("--helaui-rain-drift", `${randomBetween(-40, 40).toFixed(0)}px`);
|
|
486
|
+
drop.appendChild(img);
|
|
449
487
|
return drop;
|
|
450
488
|
}
|
|
451
489
|
/** Removes the background from the DOM. */
|
|
452
490
|
destroy() {
|
|
453
491
|
this.element.remove();
|
|
492
|
+
for (const url of this.objectUrls) URL.revokeObjectURL(url);
|
|
493
|
+
this.objectUrls.length = 0;
|
|
454
494
|
}
|
|
455
495
|
};
|
|
456
496
|
var DEFAULT_RAIN_IMAGES = ["fritter.png", "swirl.png", "kokis.png", "leaf.png"];
|
package/dist/index.d.cts
CHANGED
|
@@ -162,6 +162,7 @@ interface RainBackgroundOptions {
|
|
|
162
162
|
/** Full-screen animated background with falling image particles. */
|
|
163
163
|
declare class RainBackground {
|
|
164
164
|
readonly element: HTMLDivElement;
|
|
165
|
+
private readonly objectUrls;
|
|
165
166
|
constructor(options?: RainBackgroundOptions);
|
|
166
167
|
private createDrop;
|
|
167
168
|
/** Removes the background from the DOM. */
|
package/dist/index.d.ts
CHANGED
|
@@ -162,6 +162,7 @@ interface RainBackgroundOptions {
|
|
|
162
162
|
/** Full-screen animated background with falling image particles. */
|
|
163
163
|
declare class RainBackground {
|
|
164
164
|
readonly element: HTMLDivElement;
|
|
165
|
+
private readonly objectUrls;
|
|
165
166
|
constructor(options?: RainBackgroundOptions);
|
|
166
167
|
private createDrop;
|
|
167
168
|
/** Removes the background from the DOM. */
|
package/dist/index.js
CHANGED
|
@@ -352,16 +352,43 @@ function pick(items) {
|
|
|
352
352
|
return items[Math.floor(Math.random() * items.length)];
|
|
353
353
|
}
|
|
354
354
|
function toImageUrl(source) {
|
|
355
|
-
|
|
355
|
+
if (typeof source === "string") return source;
|
|
356
|
+
if (source && typeof source === "object") {
|
|
357
|
+
const value = source;
|
|
358
|
+
if (typeof value.src === "string") return value.src;
|
|
359
|
+
if (typeof value.default === "string") return value.default;
|
|
360
|
+
if (value.default && typeof value.default === "object" && value.default !== null) {
|
|
361
|
+
const nested = value.default;
|
|
362
|
+
if (typeof nested.src === "string") return nested.src;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
throw new Error("RainBackground image must be a URL string.");
|
|
366
|
+
}
|
|
367
|
+
function toDisplayUrl(source, cache) {
|
|
368
|
+
const existing = cache.get(source);
|
|
369
|
+
if (existing) return existing;
|
|
370
|
+
if (!source.startsWith("data:")) return source;
|
|
371
|
+
const comma = source.indexOf(",");
|
|
372
|
+
if (comma === -1) return source;
|
|
373
|
+
const header = source.slice(0, comma);
|
|
374
|
+
const data = source.slice(comma + 1);
|
|
375
|
+
const mime = header.match(/^data:([^;,]+)/i)?.[1] ?? "image/png";
|
|
376
|
+
const binary = atob(data);
|
|
377
|
+
const bytes = new Uint8Array(binary.length);
|
|
378
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
379
|
+
const url = URL.createObjectURL(new Blob([bytes], { type: mime }));
|
|
380
|
+
cache.set(source, url);
|
|
381
|
+
return url;
|
|
356
382
|
}
|
|
357
383
|
var RainBackground = class {
|
|
358
384
|
element;
|
|
385
|
+
objectUrls = [];
|
|
359
386
|
constructor(options = {}) {
|
|
360
387
|
const {
|
|
361
388
|
images = [...BUILTIN_RAIN_IMAGES],
|
|
362
389
|
density = 28,
|
|
363
390
|
minSize = 28,
|
|
364
|
-
maxSize =
|
|
391
|
+
maxSize = 72,
|
|
365
392
|
minDuration = 8,
|
|
366
393
|
maxDuration = 18,
|
|
367
394
|
target = document.body,
|
|
@@ -374,8 +401,11 @@ var RainBackground = class {
|
|
|
374
401
|
this.element.className = "helaui-rain-background";
|
|
375
402
|
this.element.setAttribute("aria-hidden", "true");
|
|
376
403
|
this.element.style.zIndex = String(zIndex);
|
|
404
|
+
const urlCache = /* @__PURE__ */ new Map();
|
|
405
|
+
const resolved = images.map((image) => toDisplayUrl(toImageUrl(image), urlCache));
|
|
406
|
+
this.objectUrls.push(...new Set(urlCache.values()));
|
|
377
407
|
for (let index = 0; index < density; index += 1) {
|
|
378
|
-
this.element.appendChild(this.createDrop(
|
|
408
|
+
this.element.appendChild(this.createDrop(resolved, minSize, maxSize, minDuration, maxDuration));
|
|
379
409
|
}
|
|
380
410
|
target.prepend(this.element);
|
|
381
411
|
}
|
|
@@ -383,9 +413,16 @@ var RainBackground = class {
|
|
|
383
413
|
const drop = document.createElement("div");
|
|
384
414
|
const duration = randomBetween(minDuration, maxDuration);
|
|
385
415
|
const size = randomBetween(minSize, maxSize);
|
|
386
|
-
const src =
|
|
416
|
+
const src = pick(images);
|
|
417
|
+
const img = document.createElement("img");
|
|
418
|
+
img.className = "helaui-rain-background__image";
|
|
419
|
+
img.src = src;
|
|
420
|
+
img.alt = "";
|
|
421
|
+
img.draggable = false;
|
|
422
|
+
img.decoding = "async";
|
|
423
|
+
img.loading = "eager";
|
|
424
|
+
img.setAttribute("fetchpriority", "high");
|
|
387
425
|
drop.className = "helaui-rain-background__drop";
|
|
388
|
-
drop.style.backgroundImage = `url(${JSON.stringify(src)})`;
|
|
389
426
|
drop.style.setProperty("--helaui-rain-x", `${randomBetween(0, 100)}%`);
|
|
390
427
|
drop.style.setProperty("--helaui-rain-size", `${size}px`);
|
|
391
428
|
drop.style.setProperty("--helaui-rain-duration", `${duration.toFixed(2)}s`);
|
|
@@ -393,11 +430,14 @@ var RainBackground = class {
|
|
|
393
430
|
drop.style.setProperty("--helaui-rain-rotation", `${randomBetween(-35, 35).toFixed(1)}deg`);
|
|
394
431
|
drop.style.setProperty("--helaui-rain-spin", `${randomBetween(90, 360).toFixed(0)}deg`);
|
|
395
432
|
drop.style.setProperty("--helaui-rain-drift", `${randomBetween(-40, 40).toFixed(0)}px`);
|
|
433
|
+
drop.appendChild(img);
|
|
396
434
|
return drop;
|
|
397
435
|
}
|
|
398
436
|
/** Removes the background from the DOM. */
|
|
399
437
|
destroy() {
|
|
400
438
|
this.element.remove();
|
|
439
|
+
for (const url of this.objectUrls) URL.revokeObjectURL(url);
|
|
440
|
+
this.objectUrls.length = 0;
|
|
401
441
|
}
|
|
402
442
|
};
|
|
403
443
|
var DEFAULT_RAIN_IMAGES = ["fritter.png", "swirl.png", "kokis.png", "leaf.png"];
|
package/dist/rain-background.css
CHANGED
|
@@ -17,9 +17,14 @@
|
|
|
17
17
|
animation: helaui-rain-fall var(--helaui-rain-duration, 10s) linear infinite;
|
|
18
18
|
animation-delay: var(--helaui-rain-delay, 0s);
|
|
19
19
|
transform: rotate(var(--helaui-rain-rotation, 0deg));
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.helaui-rain-background__image {
|
|
23
|
+
display: block;
|
|
24
|
+
width: 100%;
|
|
25
|
+
height: 100%;
|
|
26
|
+
object-fit: contain;
|
|
27
|
+
pointer-events: none;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
@keyframes helaui-rain-fall {
|