@gigo-ui/components 1.0.0 → 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.
- package/README.md +158 -42
- package/dist/components/chaos/BlackHoleSink.svelte +81 -0
- package/dist/components/chaos/BlackHoleSink.svelte.d.ts +16 -0
- package/dist/components/chaos/GigoCompactor.svelte +103 -0
- package/dist/components/chaos/GigoCompactor.svelte.d.ts +16 -0
- package/dist/components/chaos/PixelDissolve.svelte +81 -0
- package/dist/components/chaos/PixelDissolve.svelte.d.ts +16 -0
- package/dist/components/chaos/ShatterPane.svelte +84 -0
- package/dist/components/chaos/ShatterPane.svelte.d.ts +16 -0
- package/dist/components/chaos/internal/BlackHoleEngine.svelte +167 -0
- package/dist/components/chaos/internal/BlackHoleEngine.svelte.d.ts +11 -0
- package/dist/components/chaos/internal/CompactorEngine.svelte +195 -0
- package/dist/components/chaos/internal/CompactorEngine.svelte.d.ts +11 -0
- package/dist/components/chaos/internal/PixelDissolveEngine.svelte +168 -0
- package/dist/components/chaos/internal/PixelDissolveEngine.svelte.d.ts +11 -0
- package/dist/components/chaos/internal/ShatterEngine.svelte +208 -0
- package/dist/components/chaos/internal/ShatterEngine.svelte.d.ts +11 -0
- package/dist/docs/component-data.js +8 -0
- package/dist/docs/components/chaos/black-hole-sink.d.ts +2 -0
- package/dist/docs/components/chaos/black-hole-sink.js +71 -0
- package/dist/docs/components/chaos/gigo-compactor.d.ts +2 -0
- package/dist/docs/components/chaos/gigo-compactor.js +71 -0
- package/dist/docs/components/chaos/pixel-dissolve.d.ts +2 -0
- package/dist/docs/components/chaos/pixel-dissolve.js +71 -0
- package/dist/docs/components/chaos/shatter-pane.d.ts +2 -0
- package/dist/docs/components/chaos/shatter-pane.js +71 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -0
- package/dist/styles/globals.css +3 -0
- package/dist/types/index.d.ts +32 -0
- package/dist/utils/destruction-engine.d.ts +26 -0
- package/dist/utils/destruction-engine.js +75 -0
- package/package.json +73 -61
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<!-- Voronoi glass shatter — Canvas2D physics -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { onMount, onDestroy } from 'svelte';
|
|
4
|
+
import {
|
|
5
|
+
mountPortalCanvas,
|
|
6
|
+
loadImage,
|
|
7
|
+
rand,
|
|
8
|
+
randSign,
|
|
9
|
+
} from '../../../utils/destruction-engine.js';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
snapshot: string;
|
|
13
|
+
rect: DOMRect;
|
|
14
|
+
shardCount?: number;
|
|
15
|
+
intensity?: number;
|
|
16
|
+
tint?: string; // CSS colour for glass tint overlay
|
|
17
|
+
debugMode?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
snapshot,
|
|
22
|
+
rect,
|
|
23
|
+
shardCount = 48,
|
|
24
|
+
intensity = 7,
|
|
25
|
+
tint = 'rgba(180,220,255,0.08)',
|
|
26
|
+
debugMode = false,
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
let destroyed = false;
|
|
30
|
+
let rafId = 0;
|
|
31
|
+
let disposeCallbacks: Array<() => void> = [];
|
|
32
|
+
|
|
33
|
+
// approximate Voronoi cells from random seeds
|
|
34
|
+
function generateShards(w: number, h: number, n: number) {
|
|
35
|
+
// Random seed points
|
|
36
|
+
const seeds = Array.from({ length: n }, () => ({ x: rand(0, w), y: rand(0, h) }));
|
|
37
|
+
|
|
38
|
+
// Simple Lloyd's relaxation (1 pass) for better distribution
|
|
39
|
+
const relaxed = seeds.map((s) => {
|
|
40
|
+
const neighbors = seeds.filter((o) => {
|
|
41
|
+
const dx = o.x - s.x, dy = o.y - s.y;
|
|
42
|
+
return dx * dx + dy * dy < (w * h) / n * 4;
|
|
43
|
+
});
|
|
44
|
+
const cx = neighbors.reduce((a, b) => a + b.x, 0) / neighbors.length;
|
|
45
|
+
const cy = neighbors.reduce((a, b) => a + b.y, 0) / neighbors.length;
|
|
46
|
+
return { x: (s.x + cx) / 2, y: (s.y + cy) / 2 };
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// For each seed, approximate its Voronoi cell with 16 boundary samples
|
|
50
|
+
return relaxed.map((seed) => {
|
|
51
|
+
const angles = Array.from({ length: 16 }, (_, i) => (i / 16) * Math.PI * 2);
|
|
52
|
+
const verts = angles.map((angle) => {
|
|
53
|
+
// March outward until we're closer to another seed
|
|
54
|
+
let r = 2;
|
|
55
|
+
const step = Math.max(w, h) / 100;
|
|
56
|
+
while (r < Math.max(w, h)) {
|
|
57
|
+
const px = seed.x + Math.cos(angle) * r;
|
|
58
|
+
const py = seed.y + Math.sin(angle) * r;
|
|
59
|
+
const distSelf = (px - seed.x) ** 2 + (py - seed.y) ** 2;
|
|
60
|
+
const closerExists = relaxed.some((other) => {
|
|
61
|
+
if (other === seed) return false;
|
|
62
|
+
return (px - other.x) ** 2 + (py - other.y) ** 2 < distSelf;
|
|
63
|
+
});
|
|
64
|
+
if (closerExists) return { x: seed.x + Math.cos(angle) * (r - step), y: seed.y + Math.sin(angle) * (r - step) };
|
|
65
|
+
r += step;
|
|
66
|
+
}
|
|
67
|
+
return { x: seed.x + Math.cos(angle) * r, y: seed.y + Math.sin(angle) * r };
|
|
68
|
+
});
|
|
69
|
+
return { seed, verts };
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
interface PhysicsShard {
|
|
75
|
+
verts: Array<{ x: number; y: number }>;
|
|
76
|
+
cx: number; cy: number;
|
|
77
|
+
vx: number; vy: number;
|
|
78
|
+
angV: number;
|
|
79
|
+
angle: number;
|
|
80
|
+
opacity: number;
|
|
81
|
+
// snapshot clip region for this shard
|
|
82
|
+
imgOffX: number; imgOffY: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onMount(async () => {
|
|
86
|
+
const vw = window.innerWidth;
|
|
87
|
+
const vh = window.innerHeight;
|
|
88
|
+
const dpr = window.devicePixelRatio ?? 1;
|
|
89
|
+
|
|
90
|
+
const canvas = mountPortalCanvas(vw * dpr, vh * dpr);
|
|
91
|
+
canvas.style.width = '100vw';
|
|
92
|
+
canvas.style.height = '100vh';
|
|
93
|
+
disposeCallbacks.push(() => canvas.remove());
|
|
94
|
+
|
|
95
|
+
const ctx = canvas.getContext('2d')!;
|
|
96
|
+
ctx.scale(dpr, dpr);
|
|
97
|
+
|
|
98
|
+
const img = await loadImage(snapshot);
|
|
99
|
+
|
|
100
|
+
// Scale rect to fit canvas coord space (canvas is already dpr-scaled via ctx.scale)
|
|
101
|
+
const rx = rect.left, ry = rect.top, rw = rect.width, rh = rect.height;
|
|
102
|
+
|
|
103
|
+
const rawShards = generateShards(rw, rh, shardCount);
|
|
104
|
+
const g = intensity / 10; // gravity multiplier 0–1
|
|
105
|
+
|
|
106
|
+
// Build physics state
|
|
107
|
+
const shards: PhysicsShard[] = rawShards.map(({ seed, verts }) => {
|
|
108
|
+
const cx = rx + seed.x;
|
|
109
|
+
const cy = ry + seed.y;
|
|
110
|
+
// Outward impulse from element centre
|
|
111
|
+
const ecx = rx + rw / 2;
|
|
112
|
+
const ecy = ry + rh / 2;
|
|
113
|
+
const dx = cx - ecx, dy = cy - ecy;
|
|
114
|
+
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
115
|
+
const speed = rand(1, 5) * (1 + g * 3);
|
|
116
|
+
return {
|
|
117
|
+
verts: verts.map((v) => ({ x: rx + v.x, y: ry + v.y })),
|
|
118
|
+
cx, cy,
|
|
119
|
+
vx: (dx / dist) * speed,
|
|
120
|
+
vy: (dy / dist) * speed * 0.5 - rand(0, 3),
|
|
121
|
+
angV: randSign() * rand(0.005, 0.04) * (1 + g),
|
|
122
|
+
angle: 0,
|
|
123
|
+
opacity: 1,
|
|
124
|
+
imgOffX: rx,
|
|
125
|
+
imgOffY: ry,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
let startTime: number | null = null;
|
|
130
|
+
|
|
131
|
+
function tick(ts: number) {
|
|
132
|
+
if (destroyed) return;
|
|
133
|
+
if (startTime === null) startTime = ts;
|
|
134
|
+
|
|
135
|
+
const elapsed = (ts - startTime) / 1000; // seconds
|
|
136
|
+
|
|
137
|
+
ctx.clearRect(0, 0, vw, vh);
|
|
138
|
+
|
|
139
|
+
let allSettled = true;
|
|
140
|
+
|
|
141
|
+
shards.forEach((s) => {
|
|
142
|
+
// Physics step
|
|
143
|
+
s.vy += 9.81 * intensity * 0.05; // gravity
|
|
144
|
+
s.cx += s.vx;
|
|
145
|
+
s.cy += s.vy;
|
|
146
|
+
s.angle += s.angV;
|
|
147
|
+
s.vx *= 0.995; // air resistance
|
|
148
|
+
s.vy *= 0.995;
|
|
149
|
+
s.angV *= 0.99;
|
|
150
|
+
|
|
151
|
+
// Floor bounce-then-settle
|
|
152
|
+
if (s.cy > vh + 200) {
|
|
153
|
+
s.opacity = Math.max(0, s.opacity - 0.02);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (s.opacity > 0) allSettled = false;
|
|
157
|
+
|
|
158
|
+
if (s.opacity <= 0) return;
|
|
159
|
+
|
|
160
|
+
ctx.save();
|
|
161
|
+
ctx.translate(s.cx, s.cy);
|
|
162
|
+
ctx.rotate(s.angle);
|
|
163
|
+
ctx.translate(-s.cx, -s.cy);
|
|
164
|
+
ctx.globalAlpha = s.opacity;
|
|
165
|
+
|
|
166
|
+
// Clip to shard polygon
|
|
167
|
+
ctx.beginPath();
|
|
168
|
+
s.verts.forEach((v, i) => {
|
|
169
|
+
if (i === 0) ctx.moveTo(v.x, v.y);
|
|
170
|
+
else ctx.lineTo(v.x, v.y);
|
|
171
|
+
});
|
|
172
|
+
ctx.closePath();
|
|
173
|
+
ctx.clip();
|
|
174
|
+
|
|
175
|
+
// Draw snapshot slice
|
|
176
|
+
ctx.drawImage(img, -s.imgOffX + s.cx - s.cx, -s.imgOffY + s.cy - s.cy,
|
|
177
|
+
// Actually simply draw full image and let clip do the work
|
|
178
|
+
rw, rh,
|
|
179
|
+
rx - s.imgOffX + 0, ry - s.imgOffY + 0, // offset is already in verts
|
|
180
|
+
rw, rh
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Tint overlay (glass effect)
|
|
184
|
+
ctx.fillStyle = tint;
|
|
185
|
+
ctx.fill();
|
|
186
|
+
|
|
187
|
+
// Crack line highlight
|
|
188
|
+
ctx.strokeStyle = 'rgba(255,255,255,0.35)';
|
|
189
|
+
ctx.lineWidth = 0.8;
|
|
190
|
+
ctx.stroke();
|
|
191
|
+
|
|
192
|
+
ctx.restore();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (!allSettled) {
|
|
196
|
+
rafId = requestAnimationFrame(tick);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
rafId = requestAnimationFrame(tick);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
onDestroy(() => {
|
|
204
|
+
destroyed = true;
|
|
205
|
+
cancelAnimationFrame(rafId);
|
|
206
|
+
disposeCallbacks.forEach((fn) => fn());
|
|
207
|
+
});
|
|
208
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
snapshot: string;
|
|
3
|
+
rect: DOMRect;
|
|
4
|
+
shardCount?: number;
|
|
5
|
+
intensity?: number;
|
|
6
|
+
tint?: string;
|
|
7
|
+
debugMode?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare const ShatterEngine: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type ShatterEngine = ReturnType<typeof ShatterEngine>;
|
|
11
|
+
export default ShatterEngine;
|
|
@@ -12,6 +12,10 @@ import { colorPickerWrong } from "./components/badui/color-picker-wrong.js";
|
|
|
12
12
|
import { termsSidescroll } from "./components/badui/terms-sidescroll.js";
|
|
13
13
|
import { chaosButton } from "./components/chaos/chaos-button.js";
|
|
14
14
|
import { chaosForm } from "./components/chaos/chaos-form.js";
|
|
15
|
+
import { gigoCompactor } from "./components/chaos/gigo-compactor.js";
|
|
16
|
+
import { shatterPane } from "./components/chaos/shatter-pane.js";
|
|
17
|
+
import { pixelDissolve } from "./components/chaos/pixel-dissolve.js";
|
|
18
|
+
import { blackHoleSink } from "./components/chaos/black-hole-sink.js";
|
|
15
19
|
import { button } from "./components/standard/button.js";
|
|
16
20
|
import { input } from "./components/standard/input.js";
|
|
17
21
|
import { modal } from "./components/standard/modal.js";
|
|
@@ -33,6 +37,10 @@ export const componentDocs = [
|
|
|
33
37
|
termsSidescroll,
|
|
34
38
|
chaosButton,
|
|
35
39
|
chaosForm,
|
|
40
|
+
gigoCompactor,
|
|
41
|
+
shatterPane,
|
|
42
|
+
pixelDissolve,
|
|
43
|
+
blackHoleSink,
|
|
36
44
|
button,
|
|
37
45
|
input,
|
|
38
46
|
modal,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const blackHoleSink = {
|
|
2
|
+
name: "BlackHoleSink",
|
|
3
|
+
slug: "black-hole-sink",
|
|
4
|
+
emoji: "🌑",
|
|
5
|
+
category: "chaos",
|
|
6
|
+
tagline: "Swallows your UI into a gravitational singularity",
|
|
7
|
+
description: "Captures the slot element as pixels and draws each one as a particle that spirals inward toward the element's centre with exponentially growing gravitational pull. Features a shimmering accretion-disk glow ring, a growing dark core, and gravitational redshift colouring (particles heat to orange-red as they approach the event horizon). Pure Canvas2D — broad compatibility, zero WebGL.",
|
|
8
|
+
usage: `<script lang="ts">
|
|
9
|
+
import { BlackHoleSink } from '@gigo-ui/components';
|
|
10
|
+
|
|
11
|
+
let hole: BlackHoleSink;
|
|
12
|
+
let eaten = $state(false);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- Reactive -->
|
|
16
|
+
<BlackHoleSink bind:isConsumed={eaten} intensity={8} glowColor="rgba(255,120,0,0.7)">
|
|
17
|
+
<Dashboard />
|
|
18
|
+
</BlackHoleSink>
|
|
19
|
+
<button onclick={() => eaten = true}>Consume</button>
|
|
20
|
+
|
|
21
|
+
<!-- Imperative -->
|
|
22
|
+
<BlackHoleSink bind:this={hole}>
|
|
23
|
+
<Widget />
|
|
24
|
+
</BlackHoleSink>
|
|
25
|
+
<button onclick={() => hole.consume()}>Eat</button>
|
|
26
|
+
<button onclick={() => hole.restore()}>Restore</button>`,
|
|
27
|
+
props: [
|
|
28
|
+
{
|
|
29
|
+
name: "isConsumed",
|
|
30
|
+
type: "boolean",
|
|
31
|
+
default: "false",
|
|
32
|
+
description: "Trigger event horizon",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "resolution",
|
|
36
|
+
type: "number",
|
|
37
|
+
default: "4",
|
|
38
|
+
description: "Pixel sampling density",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "intensity",
|
|
42
|
+
type: "number",
|
|
43
|
+
default: "7",
|
|
44
|
+
description: "Gravitational pull 0–10",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "glowColor",
|
|
48
|
+
type: "string",
|
|
49
|
+
default: "'rgba(255,120,0,0.7)'",
|
|
50
|
+
description: "Accretion disk CSS colour",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "debugMode",
|
|
54
|
+
type: "boolean",
|
|
55
|
+
default: "false",
|
|
56
|
+
description: "Debug logging",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "class",
|
|
60
|
+
type: "string",
|
|
61
|
+
default: "''",
|
|
62
|
+
description: "Additional CSS classes",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "children",
|
|
66
|
+
type: "Snippet",
|
|
67
|
+
default: "required",
|
|
68
|
+
description: "Slot content to consume",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const gigoCompactor = {
|
|
2
|
+
name: "GigoCompactor",
|
|
3
|
+
slug: "gigo-compactor",
|
|
4
|
+
emoji: "💥",
|
|
5
|
+
category: "chaos",
|
|
6
|
+
tagline: "Voxel-crushes your UI into 3-D physics debris",
|
|
7
|
+
description: "Renders any slot content normally. On activation it captures a pixel-perfect screenshot, hides the original DOM node (preserving layout), then teleports an instanced Three.js mesh onto document.body and simulates a demolition implosion followed by gravity-driven collapse. Shards persist on scroll — once broken, stays broken. Uses Rapier3D for realistic rigid-body physics and a custom GLSL shader so every shard displays its exact slice of your UI texture.",
|
|
8
|
+
usage: `<script lang="ts">
|
|
9
|
+
import { GigoCompactor } from '@gigo-ui/components';
|
|
10
|
+
|
|
11
|
+
let compactor: GigoCompactor;
|
|
12
|
+
let crushed = $state(false);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- Reactive: setting the prop triggers the effect -->
|
|
16
|
+
<GigoCompactor bind:isGarbage={crushed} cols={24} rows={24} intensity={8}>
|
|
17
|
+
<div class="card">Fragile content</div>
|
|
18
|
+
</GigoCompactor>
|
|
19
|
+
<button onclick={() => crushed = true}>Demolish</button>
|
|
20
|
+
|
|
21
|
+
<!-- Imperative: call methods directly -->
|
|
22
|
+
<GigoCompactor bind:this={compactor}>
|
|
23
|
+
<div class="card">Also fragile</div>
|
|
24
|
+
</GigoCompactor>
|
|
25
|
+
<button onclick={() => compactor.crush()}>Crush</button>
|
|
26
|
+
<button onclick={() => compactor.restore()}>Restore</button>`,
|
|
27
|
+
props: [
|
|
28
|
+
{
|
|
29
|
+
name: "isGarbage",
|
|
30
|
+
type: "boolean",
|
|
31
|
+
default: "false",
|
|
32
|
+
description: "Trigger the crush",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "cols",
|
|
36
|
+
type: "number",
|
|
37
|
+
default: "20",
|
|
38
|
+
description: "Voxel grid columns",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "rows",
|
|
42
|
+
type: "number",
|
|
43
|
+
default: "20",
|
|
44
|
+
description: "Voxel grid rows",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "intensity",
|
|
48
|
+
type: "number",
|
|
49
|
+
default: "7",
|
|
50
|
+
description: "Crush force 0–10",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "debugMode",
|
|
54
|
+
type: "boolean",
|
|
55
|
+
default: "false",
|
|
56
|
+
description: "Show bounding box ring",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "class",
|
|
60
|
+
type: "string",
|
|
61
|
+
default: "''",
|
|
62
|
+
description: "Additional CSS classes",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "children",
|
|
66
|
+
type: "Snippet",
|
|
67
|
+
default: "required",
|
|
68
|
+
description: "Slot content to wrap and destroy",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const pixelDissolve = {
|
|
2
|
+
name: "PixelDissolve",
|
|
3
|
+
slug: "pixel-dissolve",
|
|
4
|
+
emoji: "✨",
|
|
5
|
+
category: "chaos",
|
|
6
|
+
tagline: "Snaps your UI into drifting pixel dust",
|
|
7
|
+
description: "Samples the slot content at configurable resolution and treats every pixel as an independent particle. Dissolution spreads as a contagion wave from random seed points — neighbouring pixels crumble in sequence for an organic, sand-eroding effect. Particles drift with gravity and fade out. Pure Canvas2D with OffscreenCanvas sampling — no WebGL needed.",
|
|
8
|
+
usage: `<script lang="ts">
|
|
9
|
+
import { PixelDissolve } from '@gigo-ui/components';
|
|
10
|
+
|
|
11
|
+
let snap: PixelDissolve;
|
|
12
|
+
let gone = $state(false);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- Reactive -->
|
|
16
|
+
<PixelDissolve bind:isDead={gone} resolution={2} waveSeed={12} intensity={6}>
|
|
17
|
+
<ProfileCard />
|
|
18
|
+
</PixelDissolve>
|
|
19
|
+
<button onclick={() => gone = true}>Snap</button>
|
|
20
|
+
|
|
21
|
+
<!-- Imperative -->
|
|
22
|
+
<PixelDissolve bind:this={snap}>
|
|
23
|
+
<ProfileCard />
|
|
24
|
+
</PixelDissolve>
|
|
25
|
+
<button onclick={() => snap.dissolve()}>Dissolve</button>
|
|
26
|
+
<button onclick={() => snap.restore()}>Restore</button>`,
|
|
27
|
+
props: [
|
|
28
|
+
{
|
|
29
|
+
name: "isDead",
|
|
30
|
+
type: "boolean",
|
|
31
|
+
default: "false",
|
|
32
|
+
description: "Trigger dissolution",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "resolution",
|
|
36
|
+
type: "number",
|
|
37
|
+
default: "3",
|
|
38
|
+
description: "Pixel group size (1=full quality, 6=coarse)",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "waveSeed",
|
|
42
|
+
type: "number",
|
|
43
|
+
default: "8",
|
|
44
|
+
description: "Number of initial infection origins",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "intensity",
|
|
48
|
+
type: "number",
|
|
49
|
+
default: "7",
|
|
50
|
+
description: "Gravity / drift speed 0–10",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "debugMode",
|
|
54
|
+
type: "boolean",
|
|
55
|
+
default: "false",
|
|
56
|
+
description: "Debug logging",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "class",
|
|
60
|
+
type: "string",
|
|
61
|
+
default: "''",
|
|
62
|
+
description: "Additional CSS classes",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "children",
|
|
66
|
+
type: "Snippet",
|
|
67
|
+
default: "required",
|
|
68
|
+
description: "Slot content to dissolve",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const shatterPane = {
|
|
2
|
+
name: "ShatterPane",
|
|
3
|
+
slug: "shatter-pane",
|
|
4
|
+
emoji: "🪟",
|
|
5
|
+
category: "chaos",
|
|
6
|
+
tagline: "Fractures your UI into Voronoi glass shards",
|
|
7
|
+
description: "Renders slot content normally. On trigger, captures a DOM snapshot and shatters it into irregular Voronoi polygons — each clipped with its own texture slice, highlighted with a glass-crack edge and an iridescent tint. Shards explode outward from their centre, spin, and fall. Pure Canvas2D (no WebGL dependency) for fast load and broad compatibility.",
|
|
8
|
+
usage: `<script lang="ts">
|
|
9
|
+
import { ShatterPane } from '@gigo-ui/components';
|
|
10
|
+
|
|
11
|
+
let pane: ShatterPane;
|
|
12
|
+
let broken = $state(false);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- Reactive -->
|
|
16
|
+
<ShatterPane bind:isShattered={broken} shardCount={60} intensity={8}>
|
|
17
|
+
<div class="card">Glass content</div>
|
|
18
|
+
</ShatterPane>
|
|
19
|
+
<button onclick={() => broken = true}>Shatter</button>
|
|
20
|
+
|
|
21
|
+
<!-- Imperative -->
|
|
22
|
+
<ShatterPane bind:this={pane}>
|
|
23
|
+
<div class="card">Also glass</div>
|
|
24
|
+
</ShatterPane>
|
|
25
|
+
<button onclick={() => pane.shatter()}>Break</button>
|
|
26
|
+
<button onclick={() => pane.restore()}>Fix</button>`,
|
|
27
|
+
props: [
|
|
28
|
+
{
|
|
29
|
+
name: "isShattered",
|
|
30
|
+
type: "boolean",
|
|
31
|
+
default: "false",
|
|
32
|
+
description: "Trigger fracture",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "shardCount",
|
|
36
|
+
type: "number",
|
|
37
|
+
default: "48",
|
|
38
|
+
description: "Number of Voronoi polygons",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "intensity",
|
|
42
|
+
type: "number",
|
|
43
|
+
default: "7",
|
|
44
|
+
description: "Explosion force 0–10",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "tint",
|
|
48
|
+
type: "string",
|
|
49
|
+
default: "'rgba(180,220,255,0.08)'",
|
|
50
|
+
description: "Glass iridescence CSS colour",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "debugMode",
|
|
54
|
+
type: "boolean",
|
|
55
|
+
default: "false",
|
|
56
|
+
description: "Debug logging",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "class",
|
|
60
|
+
type: "string",
|
|
61
|
+
default: "''",
|
|
62
|
+
description: "Additional CSS classes",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "children",
|
|
66
|
+
type: "Snippet",
|
|
67
|
+
default: "required",
|
|
68
|
+
description: "Slot content to wrap and shatter",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -18,5 +18,9 @@ export { default as VolumeSlider } from "./components/chaos/VolumeSlider.svelte"
|
|
|
18
18
|
export { default as ProgressDoom } from "./components/chaos/ProgressDoom.svelte";
|
|
19
19
|
export { default as ColorPickerWrong } from "./components/chaos/ColorPickerWrong.svelte";
|
|
20
20
|
export { default as TermsSidescroll } from "./components/chaos/TermsSidescroll.svelte";
|
|
21
|
-
export
|
|
21
|
+
export { default as GigoCompactor } from "./components/chaos/GigoCompactor.svelte";
|
|
22
|
+
export { default as ShatterPane } from "./components/chaos/ShatterPane.svelte";
|
|
23
|
+
export { default as PixelDissolve } from "./components/chaos/PixelDissolve.svelte";
|
|
24
|
+
export { default as BlackHoleSink } from "./components/chaos/BlackHoleSink.svelte";
|
|
25
|
+
export type { GigoBaseProps, GigoButtonProps, GigoInputProps, GigoModalProps, GigoCardProps, GigoCarouselProps, GigoFormProps, GigoNavProps, GigoChaosButtonProps, GigoChaosFormProps, GigoGhostCardProps, GigoGravityInputProps, GigoRotaryDialProps, GigoSliderPhoneProps, GigoCatchSubmitProps, GigoDropdownCalcProps, GigoPasswordPeepholeProps, GigoVolumeSliderProps, GigoProgressDoomProps, GigoColorPickerWrongProps, GigoTermsSidescrollProps, GigoCompactorProps, ShatterPaneProps, PixelDissolveProps, BlackHoleSinkProps, ButtonVariant, ButtonSize, InputType, CarouselSlide, FormField, NavItem, ChaosIntensity, } from "./types/index.js";
|
|
22
26
|
export { cn, chaosClasses, chaosRandom, chaosPickOne, randomGarbageText, } from "./utils/cn.js";
|
package/dist/index.js
CHANGED
|
@@ -18,4 +18,8 @@ export { default as VolumeSlider } from "./components/chaos/VolumeSlider.svelte"
|
|
|
18
18
|
export { default as ProgressDoom } from "./components/chaos/ProgressDoom.svelte";
|
|
19
19
|
export { default as ColorPickerWrong } from "./components/chaos/ColorPickerWrong.svelte";
|
|
20
20
|
export { default as TermsSidescroll } from "./components/chaos/TermsSidescroll.svelte";
|
|
21
|
+
export { default as GigoCompactor } from "./components/chaos/GigoCompactor.svelte";
|
|
22
|
+
export { default as ShatterPane } from "./components/chaos/ShatterPane.svelte";
|
|
23
|
+
export { default as PixelDissolve } from "./components/chaos/PixelDissolve.svelte";
|
|
24
|
+
export { default as BlackHoleSink } from "./components/chaos/BlackHoleSink.svelte";
|
|
21
25
|
export { cn, chaosClasses, chaosRandom, chaosPickOne, randomGarbageText, } from "./utils/cn.js";
|
package/dist/styles/globals.css
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;700&display=swap");
|
|
2
2
|
@import "tailwindcss";
|
|
3
3
|
|
|
4
|
+
/* Include utility candidates from the published package when this file is imported by consumers. */
|
|
5
|
+
@source "../**/*.{svelte,js,ts}";
|
|
6
|
+
|
|
4
7
|
@theme {
|
|
5
8
|
--radius-sm: 0.25rem;
|
|
6
9
|
--radius-md: 0.5rem;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -175,3 +175,35 @@ export interface GigoTermsSidescrollProps {
|
|
|
175
175
|
class?: string;
|
|
176
176
|
}
|
|
177
177
|
export type ChaosIntensity = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
|
178
|
+
export interface GigoCompactorProps {
|
|
179
|
+
isGarbage?: boolean;
|
|
180
|
+
cols?: number;
|
|
181
|
+
rows?: number;
|
|
182
|
+
intensity?: number;
|
|
183
|
+
debugMode?: boolean;
|
|
184
|
+
class?: string;
|
|
185
|
+
}
|
|
186
|
+
export interface ShatterPaneProps {
|
|
187
|
+
isShattered?: boolean;
|
|
188
|
+
shardCount?: number;
|
|
189
|
+
intensity?: number;
|
|
190
|
+
tint?: string;
|
|
191
|
+
debugMode?: boolean;
|
|
192
|
+
class?: string;
|
|
193
|
+
}
|
|
194
|
+
export interface PixelDissolveProps {
|
|
195
|
+
isDead?: boolean;
|
|
196
|
+
resolution?: number;
|
|
197
|
+
waveSeed?: number;
|
|
198
|
+
intensity?: number;
|
|
199
|
+
debugMode?: boolean;
|
|
200
|
+
class?: string;
|
|
201
|
+
}
|
|
202
|
+
export interface BlackHoleSinkProps {
|
|
203
|
+
isConsumed?: boolean;
|
|
204
|
+
resolution?: number;
|
|
205
|
+
intensity?: number;
|
|
206
|
+
glowColor?: string;
|
|
207
|
+
debugMode?: boolean;
|
|
208
|
+
class?: string;
|
|
209
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface DOMRect4 {
|
|
2
|
+
left: number;
|
|
3
|
+
top: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function domToNDC(x: number, y: number, viewportW: number, viewportH: number): [number, number];
|
|
8
|
+
export declare function cellToNDC(rect: DOMRect4, col: number, row: number, cols: number, rows: number, viewportW: number, viewportH: number): [number, number];
|
|
9
|
+
export declare function scrollAwareY(domY: number, scrollY: number): number;
|
|
10
|
+
export declare function cellUV(col: number, row: number, cols: number, rows: number): {
|
|
11
|
+
offsetX: number;
|
|
12
|
+
offsetY: number;
|
|
13
|
+
repeatX: number;
|
|
14
|
+
repeatY: number;
|
|
15
|
+
};
|
|
16
|
+
export interface SnapshotResult {
|
|
17
|
+
dataURL: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
rect: DOMRect;
|
|
21
|
+
}
|
|
22
|
+
export declare function captureSnapshot(el: HTMLElement): Promise<SnapshotResult>;
|
|
23
|
+
export declare function loadImage(src: string): Promise<HTMLImageElement>;
|
|
24
|
+
export declare function mountPortalCanvas(width: number, height: number, zIndex?: number): HTMLCanvasElement;
|
|
25
|
+
export declare function rand(min: number, max: number): number;
|
|
26
|
+
export declare function randSign(): number;
|