react-grid-lights 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/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +175 -86
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -86
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -27,7 +27,9 @@ interface GridProps {
|
|
|
27
27
|
splitChance?: number;
|
|
28
28
|
/** Speed at which trails fade (lower = slower fade) */
|
|
29
29
|
trailFadeSpeed?: number;
|
|
30
|
+
/** Enable spawning particles from the bottom as well as the top */
|
|
31
|
+
bidirectional?: boolean;
|
|
30
32
|
}
|
|
31
|
-
declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, }: GridProps): react_jsx_runtime.JSX.Element;
|
|
33
|
+
declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, bidirectional, }: GridProps): react_jsx_runtime.JSX.Element;
|
|
32
34
|
|
|
33
35
|
export { Grid, type GridProps, Grid as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,9 @@ interface GridProps {
|
|
|
27
27
|
splitChance?: number;
|
|
28
28
|
/** Speed at which trails fade (lower = slower fade) */
|
|
29
29
|
trailFadeSpeed?: number;
|
|
30
|
+
/** Enable spawning particles from the bottom as well as the top */
|
|
31
|
+
bidirectional?: boolean;
|
|
30
32
|
}
|
|
31
|
-
declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, }: GridProps): react_jsx_runtime.JSX.Element;
|
|
33
|
+
declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, bidirectional, }: GridProps): react_jsx_runtime.JSX.Element;
|
|
32
34
|
|
|
33
35
|
export { Grid, type GridProps, Grid as default };
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,8 @@ function Grid({
|
|
|
42
42
|
maxTravel = 6,
|
|
43
43
|
spawnRate = 1e3,
|
|
44
44
|
splitChance = 0.3,
|
|
45
|
-
trailFadeSpeed = 0.01
|
|
45
|
+
trailFadeSpeed = 0.01,
|
|
46
|
+
bidirectional = false
|
|
46
47
|
}) {
|
|
47
48
|
const canvasRef = (0, import_react.useRef)(null);
|
|
48
49
|
const containerRef = (0, import_react.useRef)(null);
|
|
@@ -50,6 +51,7 @@ function Grid({
|
|
|
50
51
|
const edgesRef = (0, import_react.useRef)([]);
|
|
51
52
|
const adjacencyRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
52
53
|
const topNodesRef = (0, import_react.useRef)([]);
|
|
54
|
+
const bottomNodesRef = (0, import_react.useRef)([]);
|
|
53
55
|
const animationRef = (0, import_react.useRef)(0);
|
|
54
56
|
const particlesRef = (0, import_react.useRef)([]);
|
|
55
57
|
const trailsRef = (0, import_react.useRef)([]);
|
|
@@ -59,6 +61,7 @@ function Grid({
|
|
|
59
61
|
const activeEdgesRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
60
62
|
const explosionsRef = (0, import_react.useRef)([]);
|
|
61
63
|
const nodeGlowsRef = (0, import_react.useRef)([]);
|
|
64
|
+
const spawnFromTopRef = (0, import_react.useRef)(true);
|
|
62
65
|
const nodeKey = (x, y) => `${Math.round(x * 100)},${Math.round(y * 100)}`;
|
|
63
66
|
const edgeKey = (n1, n2) => {
|
|
64
67
|
const keys = [n1.key, n2.key].sort();
|
|
@@ -71,79 +74,98 @@ function Grid({
|
|
|
71
74
|
}
|
|
72
75
|
return nodesRef.current.get(key);
|
|
73
76
|
}, []);
|
|
74
|
-
const addEdge = (0, import_react.useCallback)(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
adjacencyRef.current.
|
|
84
|
-
|
|
85
|
-
if (!adjacencyRef.current.has(to.key)) {
|
|
86
|
-
adjacencyRef.current.set(to.key, []);
|
|
87
|
-
}
|
|
88
|
-
adjacencyRef.current.get(from.key).push(edge);
|
|
89
|
-
adjacencyRef.current.get(to.key).push(edge);
|
|
90
|
-
}, [getOrCreateNode]);
|
|
91
|
-
const buildSquareGraph = (0, import_react.useCallback)((width, height) => {
|
|
92
|
-
nodesRef.current.clear();
|
|
93
|
-
edgesRef.current = [];
|
|
94
|
-
adjacencyRef.current.clear();
|
|
95
|
-
topNodesRef.current = [];
|
|
96
|
-
const cols = Math.ceil(width / cellSize) + 1;
|
|
97
|
-
const rows = Math.ceil(height / cellSize) + 1;
|
|
98
|
-
for (let i = 0; i <= cols; i++) {
|
|
99
|
-
for (let j = 0; j <= rows; j++) {
|
|
100
|
-
const x = i * cellSize;
|
|
101
|
-
const y = j * cellSize;
|
|
102
|
-
if (i < cols) addEdge(x, y, x + cellSize, y);
|
|
103
|
-
if (j < rows) addEdge(x, y, x, y + cellSize);
|
|
77
|
+
const addEdge = (0, import_react.useCallback)(
|
|
78
|
+
(x1, y1, x2, y2) => {
|
|
79
|
+
const from = getOrCreateNode(x1, y1);
|
|
80
|
+
const to = getOrCreateNode(x2, y2);
|
|
81
|
+
const key = edgeKey(from, to);
|
|
82
|
+
const existingEdge = edgesRef.current.find((e) => e.key === key);
|
|
83
|
+
if (existingEdge) return;
|
|
84
|
+
const edge = { from, to, key };
|
|
85
|
+
edgesRef.current.push(edge);
|
|
86
|
+
if (!adjacencyRef.current.has(from.key)) {
|
|
87
|
+
adjacencyRef.current.set(from.key, []);
|
|
104
88
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
for (let
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
vertices.push({
|
|
129
|
-
x: cx + size * Math.cos(angle),
|
|
130
|
-
y: cy + size * Math.sin(angle)
|
|
131
|
-
});
|
|
89
|
+
if (!adjacencyRef.current.has(to.key)) {
|
|
90
|
+
adjacencyRef.current.set(to.key, []);
|
|
91
|
+
}
|
|
92
|
+
adjacencyRef.current.get(from.key).push(edge);
|
|
93
|
+
adjacencyRef.current.get(to.key).push(edge);
|
|
94
|
+
},
|
|
95
|
+
[getOrCreateNode]
|
|
96
|
+
);
|
|
97
|
+
const buildSquareGraph = (0, import_react.useCallback)(
|
|
98
|
+
(width, height) => {
|
|
99
|
+
nodesRef.current.clear();
|
|
100
|
+
edgesRef.current = [];
|
|
101
|
+
adjacencyRef.current.clear();
|
|
102
|
+
topNodesRef.current = [];
|
|
103
|
+
bottomNodesRef.current = [];
|
|
104
|
+
const cols = Math.ceil(width / cellSize) + 1;
|
|
105
|
+
const rows = Math.ceil(height / cellSize) + 1;
|
|
106
|
+
for (let i = 0; i <= cols; i++) {
|
|
107
|
+
for (let j = 0; j <= rows; j++) {
|
|
108
|
+
const x = i * cellSize;
|
|
109
|
+
const y = j * cellSize;
|
|
110
|
+
if (i < cols) addEdge(x, y, x + cellSize, y);
|
|
111
|
+
if (j < rows) addEdge(x, y, x, y + cellSize);
|
|
132
112
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
113
|
+
}
|
|
114
|
+
const maxY = rows * cellSize;
|
|
115
|
+
for (let i = 0; i <= cols; i++) {
|
|
116
|
+
const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));
|
|
117
|
+
if (topNode) topNodesRef.current.push(topNode);
|
|
118
|
+
const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));
|
|
119
|
+
if (bottomNode) bottomNodesRef.current.push(bottomNode);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
[cellSize, addEdge]
|
|
123
|
+
);
|
|
124
|
+
const buildHexGraph = (0, import_react.useCallback)(
|
|
125
|
+
(width, height) => {
|
|
126
|
+
nodesRef.current.clear();
|
|
127
|
+
edgesRef.current = [];
|
|
128
|
+
adjacencyRef.current.clear();
|
|
129
|
+
topNodesRef.current = [];
|
|
130
|
+
bottomNodesRef.current = [];
|
|
131
|
+
const size = cellSize / 2;
|
|
132
|
+
const hexWidth = Math.sqrt(3) * size;
|
|
133
|
+
const vertSpacing = size * 1.5;
|
|
134
|
+
const cols = Math.ceil(width / hexWidth) + 2;
|
|
135
|
+
const rows = Math.ceil(height / vertSpacing) + 2;
|
|
136
|
+
for (let row = -1; row < rows; row++) {
|
|
137
|
+
for (let col = -1; col < cols; col++) {
|
|
138
|
+
const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);
|
|
139
|
+
const cy = row * vertSpacing;
|
|
140
|
+
const vertices = [];
|
|
141
|
+
for (let i = 0; i < 6; i++) {
|
|
142
|
+
const angle = Math.PI / 3 * i - Math.PI / 2;
|
|
143
|
+
vertices.push({
|
|
144
|
+
x: cx + size * Math.cos(angle),
|
|
145
|
+
y: cy + size * Math.sin(angle)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
for (let i = 0; i < 6; i++) {
|
|
149
|
+
const v1 = vertices[i];
|
|
150
|
+
const v2 = vertices[(i + 1) % 6];
|
|
151
|
+
addEdge(v1.x, v1.y, v2.x, v2.y);
|
|
152
|
+
}
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
const allNodes = Array.from(nodesRef.current.values());
|
|
156
|
+
const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);
|
|
157
|
+
topNodesRef.current = visibleNodes.filter((n) => n.y <= size * 2).sort((a, b) => a.x - b.x);
|
|
158
|
+
bottomNodesRef.current = visibleNodes.filter((n) => n.y >= height - size * 2).sort((a, b) => a.x - b.x);
|
|
159
|
+
},
|
|
160
|
+
[cellSize, addEdge]
|
|
161
|
+
);
|
|
162
|
+
const getConnectedNodes = (0, import_react.useCallback)(
|
|
163
|
+
(node, excludeNode) => {
|
|
164
|
+
const edges = adjacencyRef.current.get(node.key) || [];
|
|
165
|
+
return edges.map((e) => e.from.key === node.key ? e.to : e.from).filter((n) => !excludeNode || n.key !== excludeNode.key);
|
|
166
|
+
},
|
|
167
|
+
[]
|
|
168
|
+
);
|
|
147
169
|
(0, import_react.useEffect)(() => {
|
|
148
170
|
const canvas = canvasRef.current;
|
|
149
171
|
const container = containerRef.current;
|
|
@@ -186,11 +208,24 @@ function Grid({
|
|
|
186
208
|
const drawParticles = (ctx) => {
|
|
187
209
|
for (const particle of particlesRef.current) {
|
|
188
210
|
if (particle.dead) continue;
|
|
189
|
-
const
|
|
190
|
-
|
|
211
|
+
const history = particle.history;
|
|
212
|
+
if (history.length === 0) continue;
|
|
213
|
+
const head = history[history.length - 1];
|
|
214
|
+
if (history.length >= 2) {
|
|
215
|
+
ctx.lineCap = "round";
|
|
216
|
+
ctx.lineJoin = "round";
|
|
217
|
+
ctx.strokeStyle = lightColor + "40";
|
|
218
|
+
ctx.lineWidth = 2;
|
|
219
|
+
ctx.beginPath();
|
|
220
|
+
ctx.moveTo(history[0].x, history[0].y);
|
|
221
|
+
for (let i = 1; i < history.length; i++) {
|
|
222
|
+
ctx.lineTo(history[i].x, history[i].y);
|
|
223
|
+
}
|
|
224
|
+
ctx.stroke();
|
|
225
|
+
}
|
|
191
226
|
ctx.fillStyle = lightColor;
|
|
192
227
|
ctx.beginPath();
|
|
193
|
-
ctx.arc(x, y, 3, 0, Math.PI * 2);
|
|
228
|
+
ctx.arc(head.x, head.y, 3, 0, Math.PI * 2);
|
|
194
229
|
ctx.fill();
|
|
195
230
|
}
|
|
196
231
|
};
|
|
@@ -210,7 +245,9 @@ function Grid({
|
|
|
210
245
|
explosion.radius += 0.5;
|
|
211
246
|
explosion.opacity -= 0.08;
|
|
212
247
|
}
|
|
213
|
-
explosionsRef.current = explosionsRef.current.filter(
|
|
248
|
+
explosionsRef.current = explosionsRef.current.filter(
|
|
249
|
+
(e) => e.opacity > 0
|
|
250
|
+
);
|
|
214
251
|
};
|
|
215
252
|
const createExplosion = (x, y) => {
|
|
216
253
|
explosionsRef.current.push({
|
|
@@ -251,17 +288,21 @@ function Grid({
|
|
|
251
288
|
const keys = [n1.key, n2.key].sort();
|
|
252
289
|
return `${keys[0]}-${keys[1]}`;
|
|
253
290
|
};
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
const
|
|
291
|
+
const trySpawn = (fromTop) => {
|
|
292
|
+
const sourceNodes = fromTop ? topNodesRef.current : bottomNodesRef.current;
|
|
293
|
+
const direction = fromTop ? 1 : -1;
|
|
294
|
+
if (sourceNodes.length === 0) return false;
|
|
295
|
+
const startNode = sourceNodes[Math.floor(Math.random() * sourceNodes.length)];
|
|
257
296
|
const connectedNodes = getConnectedNodes(startNode);
|
|
258
|
-
const
|
|
259
|
-
|
|
297
|
+
const preferredNodes = connectedNodes.filter(
|
|
298
|
+
(n) => fromTop ? n.y > startNode.y : n.y < startNode.y
|
|
299
|
+
);
|
|
300
|
+
const targetNodes = preferredNodes.length > 0 ? preferredNodes : connectedNodes;
|
|
260
301
|
const availableNodes = targetNodes.filter((n) => {
|
|
261
302
|
const key = makeEdgeKey(startNode, n);
|
|
262
303
|
return !activeEdgesRef.current.has(key);
|
|
263
304
|
});
|
|
264
|
-
if (availableNodes.length === 0) return;
|
|
305
|
+
if (availableNodes.length === 0) return false;
|
|
265
306
|
const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];
|
|
266
307
|
const edgeKeyStr = makeEdgeKey(startNode, targetNode);
|
|
267
308
|
activeEdgesRef.current.add(edgeKeyStr);
|
|
@@ -273,7 +314,9 @@ function Grid({
|
|
|
273
314
|
traveled: 0,
|
|
274
315
|
maxTravel: getRandomTravel(),
|
|
275
316
|
canSplit: true,
|
|
276
|
-
dead: false
|
|
317
|
+
dead: false,
|
|
318
|
+
direction,
|
|
319
|
+
history: [{ x: startNode.x, y: startNode.y }]
|
|
277
320
|
});
|
|
278
321
|
trailsRef.current.push({
|
|
279
322
|
fromX: startNode.x,
|
|
@@ -284,17 +327,39 @@ function Grid({
|
|
|
284
327
|
opacity: 1,
|
|
285
328
|
edgeKey: edgeKeyStr
|
|
286
329
|
});
|
|
330
|
+
return true;
|
|
331
|
+
};
|
|
332
|
+
const spawnParticle = () => {
|
|
333
|
+
if (bidirectional) {
|
|
334
|
+
const fromTop = spawnFromTopRef.current;
|
|
335
|
+
spawnFromTopRef.current = !spawnFromTopRef.current;
|
|
336
|
+
if (!trySpawn(fromTop)) {
|
|
337
|
+
trySpawn(!fromTop);
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
trySpawn(true);
|
|
341
|
+
}
|
|
287
342
|
};
|
|
288
343
|
const updateParticles = () => {
|
|
289
344
|
const newParticles = [];
|
|
345
|
+
const maxHistoryLength = 10;
|
|
290
346
|
for (const particle of particlesRef.current) {
|
|
291
347
|
if (particle.dead) continue;
|
|
292
348
|
particle.progress += lightSpeed * 0.02;
|
|
349
|
+
const currentX = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;
|
|
350
|
+
const currentY = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;
|
|
351
|
+
particle.history.push({ x: currentX, y: currentY });
|
|
352
|
+
if (particle.history.length > maxHistoryLength) {
|
|
353
|
+
particle.history.shift();
|
|
354
|
+
}
|
|
293
355
|
const trailIndex = trailsRef.current.findIndex(
|
|
294
356
|
(t) => t.fromX === particle.currentNode.x && t.fromY === particle.currentNode.y && t.toX === particle.targetNode.x && t.toY === particle.targetNode.y && t.progress < 1
|
|
295
357
|
);
|
|
296
358
|
if (trailIndex !== -1) {
|
|
297
|
-
trailsRef.current[trailIndex].progress = Math.min(
|
|
359
|
+
trailsRef.current[trailIndex].progress = Math.min(
|
|
360
|
+
particle.progress,
|
|
361
|
+
1
|
|
362
|
+
);
|
|
298
363
|
}
|
|
299
364
|
if (particle.progress >= 1) {
|
|
300
365
|
createNodeGlow(particle.targetNode.x, particle.targetNode.y);
|
|
@@ -307,7 +372,10 @@ function Grid({
|
|
|
307
372
|
continue;
|
|
308
373
|
}
|
|
309
374
|
const currentNode = particle.targetNode;
|
|
310
|
-
const connectedNodes = getConnectedNodes(
|
|
375
|
+
const connectedNodes = getConnectedNodes(
|
|
376
|
+
currentNode,
|
|
377
|
+
particle.currentNode
|
|
378
|
+
);
|
|
311
379
|
const availableNodes = connectedNodes.filter((n) => {
|
|
312
380
|
const key = makeEdgeKey(currentNode, n);
|
|
313
381
|
return !activeEdgesRef.current.has(key);
|
|
@@ -319,7 +387,9 @@ function Grid({
|
|
|
319
387
|
}
|
|
320
388
|
const shouldSplit = particle.canSplit && Math.random() < splitChance && availableNodes.length >= 2;
|
|
321
389
|
if (shouldSplit) {
|
|
322
|
-
const shuffled = [...availableNodes].sort(
|
|
390
|
+
const shuffled = [...availableNodes].sort(
|
|
391
|
+
() => Math.random() - 0.5
|
|
392
|
+
);
|
|
323
393
|
const target1 = shuffled[0];
|
|
324
394
|
const target2 = shuffled[1];
|
|
325
395
|
const edgeKey1 = makeEdgeKey(currentNode, target1);
|
|
@@ -347,7 +417,9 @@ function Grid({
|
|
|
347
417
|
traveled: particle.traveled,
|
|
348
418
|
maxTravel: particle.maxTravel,
|
|
349
419
|
canSplit: false,
|
|
350
|
-
dead: false
|
|
420
|
+
dead: false,
|
|
421
|
+
direction: particle.direction,
|
|
422
|
+
history: [{ x: currentNode.x, y: currentNode.y }]
|
|
351
423
|
});
|
|
352
424
|
trailsRef.current.push({
|
|
353
425
|
fromX: currentNode.x,
|
|
@@ -445,7 +517,24 @@ function Grid({
|
|
|
445
517
|
resizeObserver.disconnect();
|
|
446
518
|
cancelAnimationFrame(animationRef.current);
|
|
447
519
|
};
|
|
448
|
-
}, [
|
|
520
|
+
}, [
|
|
521
|
+
shape,
|
|
522
|
+
cellSize,
|
|
523
|
+
lineColor,
|
|
524
|
+
lineWidth,
|
|
525
|
+
animated,
|
|
526
|
+
lightColor,
|
|
527
|
+
lightSpeed,
|
|
528
|
+
minTravel,
|
|
529
|
+
maxTravel,
|
|
530
|
+
spawnRate,
|
|
531
|
+
splitChance,
|
|
532
|
+
trailFadeSpeed,
|
|
533
|
+
bidirectional,
|
|
534
|
+
buildSquareGraph,
|
|
535
|
+
buildHexGraph,
|
|
536
|
+
getConnectedNodes
|
|
537
|
+
]);
|
|
449
538
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, className: `relative w-full h-full ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("canvas", { ref: canvasRef, className: "absolute inset-0" }) });
|
|
450
539
|
}
|
|
451
540
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Grid.tsx"],"sourcesContent":["export { Grid, type GridProps } from \"./Grid\";\nexport { Grid as default } from \"./Grid\";\n","\"use client\";\n\nimport { useEffect, useRef, useCallback } from \"react\";\n\nexport interface GridProps {\n /** Grid shape: 4 for squares, 6 for hexagons */\n shape?: 4 | 6;\n /** Size of each cell in pixels */\n cellSize?: number;\n /** Color of the grid lines (use \"transparent\" to hide) */\n lineColor?: string;\n /** Width of the grid lines */\n lineWidth?: number;\n /** Additional CSS classes */\n className?: string;\n /** Enable animated light particles */\n animated?: boolean;\n /** Color of the light particles and trails */\n lightColor?: string;\n /** Speed of the light particles (1-5 recommended) */\n lightSpeed?: number;\n /** Minimum travel distance before particle dies */\n minTravel?: number;\n /** Maximum travel distance before particle dies */\n maxTravel?: number;\n /** Milliseconds between particle spawns */\n spawnRate?: number;\n /** Probability of particle splitting (0-1) */\n splitChance?: number;\n /** Speed at which trails fade (lower = slower fade) */\n trailFadeSpeed?: number;\n}\n\ninterface Node {\n x: number;\n y: number;\n key: string;\n}\n\ninterface Edge {\n from: Node;\n to: Node;\n key: string;\n}\n\ninterface Particle {\n id: number;\n currentNode: Node;\n targetNode: Node;\n progress: number;\n traveled: number;\n maxTravel: number;\n canSplit: boolean;\n dead: boolean;\n}\n\ninterface Trail {\n fromX: number;\n fromY: number;\n toX: number;\n toY: number;\n progress: number;\n opacity: number;\n edgeKey: string;\n}\n\ninterface Explosion {\n x: number;\n y: number;\n radius: number;\n maxRadius: number;\n opacity: number;\n}\n\ninterface NodeGlow {\n x: number;\n y: number;\n opacity: number;\n}\n\nexport function Grid({\n shape = 4,\n cellSize = 40,\n lineColor = \"#e5e7eb\",\n lineWidth = 1,\n className = \"\",\n animated = false,\n lightColor = \"#3b82f6\",\n lightSpeed = 2,\n minTravel = 2,\n maxTravel = 6,\n spawnRate = 1000,\n splitChance = 0.3,\n trailFadeSpeed = 0.01,\n}: GridProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const nodesRef = useRef<Map<string, Node>>(new Map());\n const edgesRef = useRef<Edge[]>([]);\n const adjacencyRef = useRef<Map<string, Edge[]>>(new Map());\n const topNodesRef = useRef<Node[]>([]);\n const animationRef = useRef<number>(0);\n const particlesRef = useRef<Particle[]>([]);\n const trailsRef = useRef<Trail[]>([]);\n const lastSpawnRef = useRef<number>(0);\n const particleIdRef = useRef<number>(0);\n const dimensionsRef = useRef({ width: 0, height: 0 });\n const activeEdgesRef = useRef<Set<string>>(new Set());\n const explosionsRef = useRef<Explosion[]>([]);\n const nodeGlowsRef = useRef<NodeGlow[]>([]);\n\n const nodeKey = (x: number, y: number) => `${Math.round(x * 100)},${Math.round(y * 100)}`;\n const edgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const getOrCreateNode = useCallback((x: number, y: number): Node => {\n const key = nodeKey(x, y);\n if (!nodesRef.current.has(key)) {\n nodesRef.current.set(key, { x, y, key });\n }\n return nodesRef.current.get(key)!;\n }, []);\n\n const addEdge = useCallback((x1: number, y1: number, x2: number, y2: number) => {\n const from = getOrCreateNode(x1, y1);\n const to = getOrCreateNode(x2, y2);\n const key = edgeKey(from, to);\n\n const existingEdge = edgesRef.current.find(e => e.key === key);\n if (existingEdge) return;\n\n const edge: Edge = { from, to, key };\n edgesRef.current.push(edge);\n\n if (!adjacencyRef.current.has(from.key)) {\n adjacencyRef.current.set(from.key, []);\n }\n if (!adjacencyRef.current.has(to.key)) {\n adjacencyRef.current.set(to.key, []);\n }\n adjacencyRef.current.get(from.key)!.push(edge);\n adjacencyRef.current.get(to.key)!.push(edge);\n }, [getOrCreateNode]);\n\n const buildSquareGraph = useCallback((width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n\n const cols = Math.ceil(width / cellSize) + 1;\n const rows = Math.ceil(height / cellSize) + 1;\n\n for (let i = 0; i <= cols; i++) {\n for (let j = 0; j <= rows; j++) {\n const x = i * cellSize;\n const y = j * cellSize;\n if (i < cols) addEdge(x, y, x + cellSize, y);\n if (j < rows) addEdge(x, y, x, y + cellSize);\n }\n }\n\n for (let i = 0; i <= cols; i++) {\n const node = nodesRef.current.get(nodeKey(i * cellSize, 0));\n if (node) topNodesRef.current.push(node);\n }\n }, [cellSize, addEdge]);\n\n const buildHexGraph = useCallback((width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n\n const size = cellSize / 2;\n const hexWidth = Math.sqrt(3) * size;\n const vertSpacing = size * 1.5;\n\n const cols = Math.ceil(width / hexWidth) + 2;\n const rows = Math.ceil(height / vertSpacing) + 2;\n\n for (let row = -1; row < rows; row++) {\n for (let col = -1; col < cols; col++) {\n const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);\n const cy = row * vertSpacing;\n\n const vertices: { x: number; y: number }[] = [];\n for (let i = 0; i < 6; i++) {\n const angle = (Math.PI / 3) * i - Math.PI / 2;\n vertices.push({\n x: cx + size * Math.cos(angle),\n y: cy + size * Math.sin(angle),\n });\n }\n\n for (let i = 0; i < 6; i++) {\n const v1 = vertices[i];\n const v2 = vertices[(i + 1) % 6];\n addEdge(v1.x, v1.y, v2.x, v2.y);\n }\n }\n }\n\n const sortedNodes = Array.from(nodesRef.current.values())\n .filter(n => n.y >= 0 && n.y <= size)\n .sort((a, b) => a.x - b.x);\n topNodesRef.current = sortedNodes;\n }, [cellSize, addEdge]);\n\n const getConnectedNodes = useCallback((node: Node, excludeNode?: Node): Node[] => {\n const edges = adjacencyRef.current.get(node.key) || [];\n return edges\n .map(e => (e.from.key === node.key ? e.to : e.from))\n .filter(n => !excludeNode || n.key !== excludeNode.key);\n }, []);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container) return;\n\n let dpr = 1;\n\n const drawGrid = (ctx: CanvasRenderingContext2D, width: number, height: number) => {\n ctx.clearRect(0, 0, width, height);\n ctx.strokeStyle = lineColor;\n ctx.lineWidth = lineWidth;\n ctx.beginPath();\n\n for (const edge of edgesRef.current) {\n ctx.moveTo(edge.from.x, edge.from.y);\n ctx.lineTo(edge.to.x, edge.to.y);\n }\n\n ctx.stroke();\n };\n\n const drawTrails = (ctx: CanvasRenderingContext2D) => {\n for (const trail of trailsRef.current) {\n if (trail.opacity <= 0) continue;\n\n const alpha = Math.floor(trail.opacity * 0.08 * 255).toString(16).padStart(2, '0');\n\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 1;\n ctx.lineCap = \"round\";\n ctx.beginPath();\n ctx.moveTo(trail.fromX, trail.fromY);\n\n if (trail.progress < 1) {\n const endX = trail.fromX + (trail.toX - trail.fromX) * trail.progress;\n const endY = trail.fromY + (trail.toY - trail.fromY) * trail.progress;\n ctx.lineTo(endX, endY);\n } else {\n ctx.lineTo(trail.toX, trail.toY);\n }\n ctx.stroke();\n\n ctx.shadowColor = lightColor;\n ctx.shadowBlur = 2;\n ctx.stroke();\n ctx.shadowBlur = 0;\n }\n };\n\n const drawParticles = (ctx: CanvasRenderingContext2D) => {\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n const x = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;\n const y = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n ctx.arc(x, y, 3, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const drawExplosions = (ctx: CanvasRenderingContext2D) => {\n for (const explosion of explosionsRef.current) {\n if (explosion.opacity <= 0) continue;\n\n const alpha = Math.floor(explosion.opacity * 255).toString(16).padStart(2, '0');\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);\n ctx.stroke();\n }\n };\n\n const updateExplosions = () => {\n for (const explosion of explosionsRef.current) {\n explosion.radius += 0.5;\n explosion.opacity -= 0.08;\n }\n explosionsRef.current = explosionsRef.current.filter(e => e.opacity > 0);\n };\n\n const createExplosion = (x: number, y: number) => {\n explosionsRef.current.push({\n x,\n y,\n radius: 2,\n maxRadius: 8,\n opacity: 1,\n });\n };\n\n const createNodeGlow = (x: number, y: number) => {\n nodeGlowsRef.current.push({\n x,\n y,\n opacity: 1,\n });\n };\n\n const drawNodeGlows = (ctx: CanvasRenderingContext2D) => {\n for (const glow of nodeGlowsRef.current) {\n if (glow.opacity <= 0) continue;\n\n const alpha = Math.floor(glow.opacity * 0.4 * 255).toString(16).padStart(2, '0');\n ctx.fillStyle = lightColor + alpha;\n ctx.beginPath();\n ctx.arc(glow.x, glow.y, 2, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const updateNodeGlows = () => {\n for (const glow of nodeGlowsRef.current) {\n glow.opacity -= trailFadeSpeed * 3;\n }\n nodeGlowsRef.current = nodeGlowsRef.current.filter(g => g.opacity > 0);\n };\n\n const getRandomTravel = () => {\n return Math.floor(Math.random() * (maxTravel - minTravel + 1)) + minTravel;\n };\n\n const makeEdgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const spawnParticle = () => {\n if (topNodesRef.current.length === 0) return;\n\n const startNode = topNodesRef.current[Math.floor(Math.random() * topNodesRef.current.length)];\n const connectedNodes = getConnectedNodes(startNode);\n\n const downwardNodes = connectedNodes.filter(n => n.y > startNode.y);\n const targetNodes = downwardNodes.length > 0 ? downwardNodes : connectedNodes;\n\n const availableNodes = targetNodes.filter(n => {\n const key = makeEdgeKey(startNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) return;\n\n const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const edgeKeyStr = makeEdgeKey(startNode, targetNode);\n activeEdgesRef.current.add(edgeKeyStr);\n\n particlesRef.current.push({\n id: particleIdRef.current++,\n currentNode: startNode,\n targetNode,\n progress: 0,\n traveled: 0,\n maxTravel: getRandomTravel(),\n canSplit: true,\n dead: false,\n });\n\n trailsRef.current.push({\n fromX: startNode.x,\n fromY: startNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKeyStr,\n });\n };\n\n const updateParticles = () => {\n const newParticles: Particle[] = [];\n\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n particle.progress += lightSpeed * 0.02;\n\n const trailIndex = trailsRef.current.findIndex(\n t => t.fromX === particle.currentNode.x &&\n t.fromY === particle.currentNode.y &&\n t.toX === particle.targetNode.x &&\n t.toY === particle.targetNode.y &&\n t.progress < 1\n );\n if (trailIndex !== -1) {\n trailsRef.current[trailIndex].progress = Math.min(particle.progress, 1);\n }\n\n if (particle.progress >= 1) {\n createNodeGlow(particle.targetNode.x, particle.targetNode.y);\n particle.traveled++;\n\n if (particle.traveled >= particle.maxTravel) {\n const x = particle.targetNode.x;\n const y = particle.targetNode.y;\n createExplosion(x, y);\n particle.dead = true;\n continue;\n }\n\n const currentNode = particle.targetNode;\n const connectedNodes = getConnectedNodes(currentNode, particle.currentNode);\n\n const availableNodes = connectedNodes.filter(n => {\n const key = makeEdgeKey(currentNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) {\n createExplosion(currentNode.x, currentNode.y);\n particle.dead = true;\n continue;\n }\n\n const shouldSplit = particle.canSplit && Math.random() < splitChance && availableNodes.length >= 2;\n\n if (shouldSplit) {\n const shuffled = [...availableNodes].sort(() => Math.random() - 0.5);\n const target1 = shuffled[0];\n const target2 = shuffled[1];\n\n const edgeKey1 = makeEdgeKey(currentNode, target1);\n const edgeKey2 = makeEdgeKey(currentNode, target2);\n activeEdgesRef.current.add(edgeKey1);\n activeEdgesRef.current.add(edgeKey2);\n\n particle.currentNode = currentNode;\n particle.targetNode = target1;\n particle.progress = 0;\n particle.canSplit = false;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target1.x,\n toY: target1.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey1,\n });\n\n newParticles.push({\n id: particleIdRef.current++,\n currentNode: currentNode,\n targetNode: target2,\n progress: 0,\n traveled: particle.traveled,\n maxTravel: particle.maxTravel,\n canSplit: false,\n dead: false,\n });\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target2.x,\n toY: target2.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey2,\n });\n } else {\n const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const newEdgeKey = makeEdgeKey(currentNode, targetNode);\n activeEdgesRef.current.add(newEdgeKey);\n\n particle.currentNode = currentNode;\n particle.targetNode = targetNode;\n particle.progress = 0;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: newEdgeKey,\n });\n }\n }\n }\n\n particlesRef.current.push(...newParticles);\n particlesRef.current = particlesRef.current.filter(p => !p.dead);\n };\n\n const updateTrails = () => {\n for (const trail of trailsRef.current) {\n if (trail.progress >= 1) {\n trail.opacity -= trailFadeSpeed;\n }\n }\n const fadedTrails = trailsRef.current.filter(t => t.opacity <= 0);\n for (const trail of fadedTrails) {\n activeEdgesRef.current.delete(trail.edgeKey);\n }\n trailsRef.current = trailsRef.current.filter(t => t.opacity > 0);\n };\n\n const animate = (timestamp: number) => {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const { width, height } = dimensionsRef.current;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n drawGrid(ctx, width, height);\n\n if (animated) {\n if (timestamp - lastSpawnRef.current > spawnRate) {\n spawnParticle();\n lastSpawnRef.current = timestamp;\n }\n\n updateParticles();\n updateTrails();\n updateExplosions();\n updateNodeGlows();\n drawTrails(ctx);\n drawNodeGlows(ctx);\n drawParticles(ctx);\n drawExplosions(ctx);\n }\n\n ctx.restore();\n animationRef.current = requestAnimationFrame(animate);\n };\n\n const setup = () => {\n const { width, height } = container.getBoundingClientRect();\n dimensionsRef.current = { width, height };\n dpr = window.devicePixelRatio || 1;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n if (shape === 4) {\n buildSquareGraph(width, height);\n } else {\n buildHexGraph(width, height);\n }\n\n particlesRef.current = [];\n trailsRef.current = [];\n activeEdgesRef.current.clear();\n explosionsRef.current = [];\n nodeGlowsRef.current = [];\n };\n\n const resizeObserver = new ResizeObserver(() => {\n setup();\n });\n\n resizeObserver.observe(container);\n setup();\n animationRef.current = requestAnimationFrame(animate);\n\n return () => {\n resizeObserver.disconnect();\n cancelAnimationFrame(animationRef.current);\n };\n }, [shape, cellSize, lineColor, lineWidth, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, buildSquareGraph, buildHexGraph, getConnectedNodes]);\n\n return (\n <div ref={containerRef} className={`relative w-full h-full ${className}`}>\n <canvas ref={canvasRef} className=\"absolute inset-0\" />\n </div>\n );\n}\n\nexport default Grid;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,mBAA+C;AA4kBzC;AA9fC,SAAS,KAAK;AAAA,EACnB,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,iBAAiB;AACnB,GAAc;AACZ,QAAM,gBAAY,qBAA0B,IAAI;AAChD,QAAM,mBAAe,qBAAuB,IAAI;AAChD,QAAM,eAAW,qBAA0B,oBAAI,IAAI,CAAC;AACpD,QAAM,eAAW,qBAAe,CAAC,CAAC;AAClC,QAAM,mBAAe,qBAA4B,oBAAI,IAAI,CAAC;AAC1D,QAAM,kBAAc,qBAAe,CAAC,CAAC;AACrC,QAAM,mBAAe,qBAAe,CAAC;AACrC,QAAM,mBAAe,qBAAmB,CAAC,CAAC;AAC1C,QAAM,gBAAY,qBAAgB,CAAC,CAAC;AACpC,QAAM,mBAAe,qBAAe,CAAC;AACrC,QAAM,oBAAgB,qBAAe,CAAC;AACtC,QAAM,oBAAgB,qBAAO,EAAE,OAAO,GAAG,QAAQ,EAAE,CAAC;AACpD,QAAM,qBAAiB,qBAAoB,oBAAI,IAAI,CAAC;AACpD,QAAM,oBAAgB,qBAAoB,CAAC,CAAC;AAC5C,QAAM,mBAAe,qBAAmB,CAAC,CAAC;AAE1C,QAAM,UAAU,CAAC,GAAW,MAAc,GAAG,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC;AACvF,QAAM,UAAU,CAAC,IAAU,OAAa;AACtC,UAAM,OAAO,CAAC,GAAG,KAAK,GAAG,GAAG,EAAE,KAAK;AACnC,WAAO,GAAG,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;AAAA,EAC9B;AAEA,QAAM,sBAAkB,0BAAY,CAAC,GAAW,MAAoB;AAClE,UAAM,MAAM,QAAQ,GAAG,CAAC;AACxB,QAAI,CAAC,SAAS,QAAQ,IAAI,GAAG,GAAG;AAC9B,eAAS,QAAQ,IAAI,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;AAAA,IACzC;AACA,WAAO,SAAS,QAAQ,IAAI,GAAG;AAAA,EACjC,GAAG,CAAC,CAAC;AAEL,QAAM,cAAU,0BAAY,CAAC,IAAY,IAAY,IAAY,OAAe;AAC9E,UAAM,OAAO,gBAAgB,IAAI,EAAE;AACnC,UAAM,KAAK,gBAAgB,IAAI,EAAE;AACjC,UAAM,MAAM,QAAQ,MAAM,EAAE;AAE5B,UAAM,eAAe,SAAS,QAAQ,KAAK,OAAK,EAAE,QAAQ,GAAG;AAC7D,QAAI,aAAc;AAElB,UAAM,OAAa,EAAE,MAAM,IAAI,IAAI;AACnC,aAAS,QAAQ,KAAK,IAAI;AAE1B,QAAI,CAAC,aAAa,QAAQ,IAAI,KAAK,GAAG,GAAG;AACvC,mBAAa,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC;AAAA,IACvC;AACA,QAAI,CAAC,aAAa,QAAQ,IAAI,GAAG,GAAG,GAAG;AACrC,mBAAa,QAAQ,IAAI,GAAG,KAAK,CAAC,CAAC;AAAA,IACrC;AACA,iBAAa,QAAQ,IAAI,KAAK,GAAG,EAAG,KAAK,IAAI;AAC7C,iBAAa,QAAQ,IAAI,GAAG,GAAG,EAAG,KAAK,IAAI;AAAA,EAC7C,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,uBAAmB,0BAAY,CAAC,OAAe,WAAmB;AACtE,aAAS,QAAQ,MAAM;AACvB,aAAS,UAAU,CAAC;AACpB,iBAAa,QAAQ,MAAM;AAC3B,gBAAY,UAAU,CAAC;AAEvB,UAAM,OAAO,KAAK,KAAK,QAAQ,QAAQ,IAAI;AAC3C,UAAM,OAAO,KAAK,KAAK,SAAS,QAAQ,IAAI;AAE5C,aAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAC9B,eAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAC9B,cAAM,IAAI,IAAI;AACd,cAAM,IAAI,IAAI;AACd,YAAI,IAAI,KAAM,SAAQ,GAAG,GAAG,IAAI,UAAU,CAAC;AAC3C,YAAI,IAAI,KAAM,SAAQ,GAAG,GAAG,GAAG,IAAI,QAAQ;AAAA,MAC7C;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAC9B,YAAM,OAAO,SAAS,QAAQ,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC;AAC1D,UAAI,KAAM,aAAY,QAAQ,KAAK,IAAI;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,QAAM,oBAAgB,0BAAY,CAAC,OAAe,WAAmB;AACnE,aAAS,QAAQ,MAAM;AACvB,aAAS,UAAU,CAAC;AACpB,iBAAa,QAAQ,MAAM;AAC3B,gBAAY,UAAU,CAAC;AAEvB,UAAM,OAAO,WAAW;AACxB,UAAM,WAAW,KAAK,KAAK,CAAC,IAAI;AAChC,UAAM,cAAc,OAAO;AAE3B,UAAM,OAAO,KAAK,KAAK,QAAQ,QAAQ,IAAI;AAC3C,UAAM,OAAO,KAAK,KAAK,SAAS,WAAW,IAAI;AAE/C,aAAS,MAAM,IAAI,MAAM,MAAM,OAAO;AACpC,eAAS,MAAM,IAAI,MAAM,MAAM,OAAO;AACpC,cAAM,KAAK,MAAM,YAAY,MAAM,MAAM,IAAI,IAAI,WAAW;AAC5D,cAAM,KAAK,MAAM;AAEjB,cAAM,WAAuC,CAAC;AAC9C,iBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,gBAAM,QAAS,KAAK,KAAK,IAAK,IAAI,KAAK,KAAK;AAC5C,mBAAS,KAAK;AAAA,YACZ,GAAG,KAAK,OAAO,KAAK,IAAI,KAAK;AAAA,YAC7B,GAAG,KAAK,OAAO,KAAK,IAAI,KAAK;AAAA,UAC/B,CAAC;AAAA,QACH;AAEA,iBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,gBAAM,KAAK,SAAS,CAAC;AACrB,gBAAM,KAAK,UAAU,IAAI,KAAK,CAAC;AAC/B,kBAAQ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,KAAK,SAAS,QAAQ,OAAO,CAAC,EACrD,OAAO,OAAK,EAAE,KAAK,KAAK,EAAE,KAAK,IAAI,EACnC,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC;AAC3B,gBAAY,UAAU;AAAA,EACxB,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,QAAM,wBAAoB,0BAAY,CAAC,MAAY,gBAA+B;AAChF,UAAM,QAAQ,aAAa,QAAQ,IAAI,KAAK,GAAG,KAAK,CAAC;AACrD,WAAO,MACJ,IAAI,OAAM,EAAE,KAAK,QAAQ,KAAK,MAAM,EAAE,KAAK,EAAE,IAAK,EAClD,OAAO,OAAK,CAAC,eAAe,EAAE,QAAQ,YAAY,GAAG;AAAA,EAC1D,GAAG,CAAC,CAAC;AAEL,8BAAU,MAAM;AACd,UAAM,SAAS,UAAU;AACzB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,UAAU,CAAC,UAAW;AAE3B,QAAI,MAAM;AAEV,UAAM,WAAW,CAAC,KAA+B,OAAe,WAAmB;AACjF,UAAI,UAAU,GAAG,GAAG,OAAO,MAAM;AACjC,UAAI,cAAc;AAClB,UAAI,YAAY;AAChB,UAAI,UAAU;AAEd,iBAAW,QAAQ,SAAS,SAAS;AACnC,YAAI,OAAO,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC;AACnC,YAAI,OAAO,KAAK,GAAG,GAAG,KAAK,GAAG,CAAC;AAAA,MACjC;AAEA,UAAI,OAAO;AAAA,IACb;AAEA,UAAM,aAAa,CAAC,QAAkC;AACpD,iBAAW,SAAS,UAAU,SAAS;AACrC,YAAI,MAAM,WAAW,EAAG;AAExB,cAAM,QAAQ,KAAK,MAAM,MAAM,UAAU,OAAO,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAEjF,YAAI,cAAc,aAAa;AAC/B,YAAI,YAAY;AAChB,YAAI,UAAU;AACd,YAAI,UAAU;AACd,YAAI,OAAO,MAAM,OAAO,MAAM,KAAK;AAEnC,YAAI,MAAM,WAAW,GAAG;AACtB,gBAAM,OAAO,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,MAAM;AAC7D,gBAAM,OAAO,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,MAAM;AAC7D,cAAI,OAAO,MAAM,IAAI;AAAA,QACvB,OAAO;AACL,cAAI,OAAO,MAAM,KAAK,MAAM,GAAG;AAAA,QACjC;AACA,YAAI,OAAO;AAEX,YAAI,cAAc;AAClB,YAAI,aAAa;AACjB,YAAI,OAAO;AACX,YAAI,aAAa;AAAA,MACnB;AAAA,IACF;AAEA,UAAM,gBAAgB,CAAC,QAAkC;AACvD,iBAAW,YAAY,aAAa,SAAS;AAC3C,YAAI,SAAS,KAAM;AAEnB,cAAM,IAAI,SAAS,YAAY,KAAK,SAAS,WAAW,IAAI,SAAS,YAAY,KAAK,SAAS;AAC/F,cAAM,IAAI,SAAS,YAAY,KAAK,SAAS,WAAW,IAAI,SAAS,YAAY,KAAK,SAAS;AAE/F,YAAI,YAAY;AAChB,YAAI,UAAU;AACd,YAAI,IAAI,GAAG,GAAG,GAAG,GAAG,KAAK,KAAK,CAAC;AAC/B,YAAI,KAAK;AAAA,MACX;AAAA,IACF;AAEA,UAAM,iBAAiB,CAAC,QAAkC;AACxD,iBAAW,aAAa,cAAc,SAAS;AAC7C,YAAI,UAAU,WAAW,EAAG;AAE5B,cAAM,QAAQ,KAAK,MAAM,UAAU,UAAU,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC9E,YAAI,cAAc,aAAa;AAC/B,YAAI,YAAY;AAChB,YAAI,UAAU;AACd,YAAI,IAAI,UAAU,GAAG,UAAU,GAAG,UAAU,QAAQ,GAAG,KAAK,KAAK,CAAC;AAClE,YAAI,OAAO;AAAA,MACb;AAAA,IACF;AAEA,UAAM,mBAAmB,MAAM;AAC7B,iBAAW,aAAa,cAAc,SAAS;AAC7C,kBAAU,UAAU;AACpB,kBAAU,WAAW;AAAA,MACvB;AACA,oBAAc,UAAU,cAAc,QAAQ,OAAO,OAAK,EAAE,UAAU,CAAC;AAAA,IACzE;AAEA,UAAM,kBAAkB,CAAC,GAAW,MAAc;AAChD,oBAAc,QAAQ,KAAK;AAAA,QACzB;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,UAAM,iBAAiB,CAAC,GAAW,MAAc;AAC/C,mBAAa,QAAQ,KAAK;AAAA,QACxB;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,CAAC,QAAkC;AACvD,iBAAW,QAAQ,aAAa,SAAS;AACvC,YAAI,KAAK,WAAW,EAAG;AAEvB,cAAM,QAAQ,KAAK,MAAM,KAAK,UAAU,MAAM,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC/E,YAAI,YAAY,aAAa;AAC7B,YAAI,UAAU;AACd,YAAI,IAAI,KAAK,GAAG,KAAK,GAAG,GAAG,GAAG,KAAK,KAAK,CAAC;AACzC,YAAI,KAAK;AAAA,MACX;AAAA,IACF;AAEA,UAAM,kBAAkB,MAAM;AAC5B,iBAAW,QAAQ,aAAa,SAAS;AACvC,aAAK,WAAW,iBAAiB;AAAA,MACnC;AACA,mBAAa,UAAU,aAAa,QAAQ,OAAO,OAAK,EAAE,UAAU,CAAC;AAAA,IACvE;AAEA,UAAM,kBAAkB,MAAM;AAC5B,aAAO,KAAK,MAAM,KAAK,OAAO,KAAK,YAAY,YAAY,EAAE,IAAI;AAAA,IACnE;AAEA,UAAM,cAAc,CAAC,IAAU,OAAa;AAC1C,YAAM,OAAO,CAAC,GAAG,KAAK,GAAG,GAAG,EAAE,KAAK;AACnC,aAAO,GAAG,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;AAAA,IAC9B;AAEA,UAAM,gBAAgB,MAAM;AAC1B,UAAI,YAAY,QAAQ,WAAW,EAAG;AAEtC,YAAM,YAAY,YAAY,QAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,YAAY,QAAQ,MAAM,CAAC;AAC5F,YAAM,iBAAiB,kBAAkB,SAAS;AAElD,YAAM,gBAAgB,eAAe,OAAO,OAAK,EAAE,IAAI,UAAU,CAAC;AAClE,YAAM,cAAc,cAAc,SAAS,IAAI,gBAAgB;AAE/D,YAAM,iBAAiB,YAAY,OAAO,OAAK;AAC7C,cAAM,MAAM,YAAY,WAAW,CAAC;AACpC,eAAO,CAAC,eAAe,QAAQ,IAAI,GAAG;AAAA,MACxC,CAAC;AAED,UAAI,eAAe,WAAW,EAAG;AAEjC,YAAM,aAAa,eAAe,KAAK,MAAM,KAAK,OAAO,IAAI,eAAe,MAAM,CAAC;AACnF,YAAM,aAAa,YAAY,WAAW,UAAU;AACpD,qBAAe,QAAQ,IAAI,UAAU;AAErC,mBAAa,QAAQ,KAAK;AAAA,QACxB,IAAI,cAAc;AAAA,QAClB,aAAa;AAAA,QACb;AAAA,QACA,UAAU;AAAA,QACV,UAAU;AAAA,QACV,WAAW,gBAAgB;AAAA,QAC3B,UAAU;AAAA,QACV,MAAM;AAAA,MACR,CAAC;AAED,gBAAU,QAAQ,KAAK;AAAA,QACrB,OAAO,UAAU;AAAA,QACjB,OAAO,UAAU;AAAA,QACjB,KAAK,WAAW;AAAA,QAChB,KAAK,WAAW;AAAA,QAChB,UAAU;AAAA,QACV,SAAS;AAAA,QACT,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,UAAM,kBAAkB,MAAM;AAC5B,YAAM,eAA2B,CAAC;AAElC,iBAAW,YAAY,aAAa,SAAS;AAC3C,YAAI,SAAS,KAAM;AAEnB,iBAAS,YAAY,aAAa;AAElC,cAAM,aAAa,UAAU,QAAQ;AAAA,UACnC,OAAK,EAAE,UAAU,SAAS,YAAY,KACjC,EAAE,UAAU,SAAS,YAAY,KACjC,EAAE,QAAQ,SAAS,WAAW,KAC9B,EAAE,QAAQ,SAAS,WAAW,KAC9B,EAAE,WAAW;AAAA,QACpB;AACA,YAAI,eAAe,IAAI;AACrB,oBAAU,QAAQ,UAAU,EAAE,WAAW,KAAK,IAAI,SAAS,UAAU,CAAC;AAAA,QACxE;AAEA,YAAI,SAAS,YAAY,GAAG;AAC1B,yBAAe,SAAS,WAAW,GAAG,SAAS,WAAW,CAAC;AAC3D,mBAAS;AAET,cAAI,SAAS,YAAY,SAAS,WAAW;AAC3C,kBAAM,IAAI,SAAS,WAAW;AAC9B,kBAAM,IAAI,SAAS,WAAW;AAC9B,4BAAgB,GAAG,CAAC;AACpB,qBAAS,OAAO;AAChB;AAAA,UACF;AAEA,gBAAM,cAAc,SAAS;AAC7B,gBAAM,iBAAiB,kBAAkB,aAAa,SAAS,WAAW;AAE1E,gBAAM,iBAAiB,eAAe,OAAO,OAAK;AAChD,kBAAM,MAAM,YAAY,aAAa,CAAC;AACtC,mBAAO,CAAC,eAAe,QAAQ,IAAI,GAAG;AAAA,UACxC,CAAC;AAED,cAAI,eAAe,WAAW,GAAG;AAC/B,4BAAgB,YAAY,GAAG,YAAY,CAAC;AAC5C,qBAAS,OAAO;AAChB;AAAA,UACF;AAEA,gBAAM,cAAc,SAAS,YAAY,KAAK,OAAO,IAAI,eAAe,eAAe,UAAU;AAEjG,cAAI,aAAa;AACf,kBAAM,WAAW,CAAC,GAAG,cAAc,EAAE,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AACnE,kBAAM,UAAU,SAAS,CAAC;AAC1B,kBAAM,UAAU,SAAS,CAAC;AAE1B,kBAAM,WAAW,YAAY,aAAa,OAAO;AACjD,kBAAM,WAAW,YAAY,aAAa,OAAO;AACjD,2BAAe,QAAQ,IAAI,QAAQ;AACnC,2BAAe,QAAQ,IAAI,QAAQ;AAEnC,qBAAS,cAAc;AACvB,qBAAS,aAAa;AACtB,qBAAS,WAAW;AACpB,qBAAS,WAAW;AAEpB,sBAAU,QAAQ,KAAK;AAAA,cACrB,OAAO,YAAY;AAAA,cACnB,OAAO,YAAY;AAAA,cACnB,KAAK,QAAQ;AAAA,cACb,KAAK,QAAQ;AAAA,cACb,UAAU;AAAA,cACV,SAAS;AAAA,cACT,SAAS;AAAA,YACX,CAAC;AAED,yBAAa,KAAK;AAAA,cAChB,IAAI,cAAc;AAAA,cAClB;AAAA,cACA,YAAY;AAAA,cACZ,UAAU;AAAA,cACV,UAAU,SAAS;AAAA,cACnB,WAAW,SAAS;AAAA,cACpB,UAAU;AAAA,cACV,MAAM;AAAA,YACR,CAAC;AAED,sBAAU,QAAQ,KAAK;AAAA,cACrB,OAAO,YAAY;AAAA,cACnB,OAAO,YAAY;AAAA,cACnB,KAAK,QAAQ;AAAA,cACb,KAAK,QAAQ;AAAA,cACb,UAAU;AAAA,cACV,SAAS;AAAA,cACT,SAAS;AAAA,YACX,CAAC;AAAA,UACH,OAAO;AACL,kBAAM,aAAa,eAAe,KAAK,MAAM,KAAK,OAAO,IAAI,eAAe,MAAM,CAAC;AACnF,kBAAM,aAAa,YAAY,aAAa,UAAU;AACtD,2BAAe,QAAQ,IAAI,UAAU;AAErC,qBAAS,cAAc;AACvB,qBAAS,aAAa;AACtB,qBAAS,WAAW;AAEpB,sBAAU,QAAQ,KAAK;AAAA,cACrB,OAAO,YAAY;AAAA,cACnB,OAAO,YAAY;AAAA,cACnB,KAAK,WAAW;AAAA,cAChB,KAAK,WAAW;AAAA,cAChB,UAAU;AAAA,cACV,SAAS;AAAA,cACT,SAAS;AAAA,YACX,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA,mBAAa,QAAQ,KAAK,GAAG,YAAY;AACzC,mBAAa,UAAU,aAAa,QAAQ,OAAO,OAAK,CAAC,EAAE,IAAI;AAAA,IACjE;AAEA,UAAM,eAAe,MAAM;AACzB,iBAAW,SAAS,UAAU,SAAS;AACrC,YAAI,MAAM,YAAY,GAAG;AACvB,gBAAM,WAAW;AAAA,QACnB;AAAA,MACF;AACA,YAAM,cAAc,UAAU,QAAQ,OAAO,OAAK,EAAE,WAAW,CAAC;AAChE,iBAAW,SAAS,aAAa;AAC/B,uBAAe,QAAQ,OAAO,MAAM,OAAO;AAAA,MAC7C;AACA,gBAAU,UAAU,UAAU,QAAQ,OAAO,OAAK,EAAE,UAAU,CAAC;AAAA,IACjE;AAEA,UAAM,UAAU,CAAC,cAAsB;AACrC,YAAM,MAAM,OAAO,WAAW,IAAI;AAClC,UAAI,CAAC,IAAK;AAEV,YAAM,EAAE,OAAO,OAAO,IAAI,cAAc;AAExC,UAAI,KAAK;AACT,UAAI,MAAM,KAAK,GAAG;AAClB,eAAS,KAAK,OAAO,MAAM;AAE3B,UAAI,UAAU;AACZ,YAAI,YAAY,aAAa,UAAU,WAAW;AAChD,wBAAc;AACd,uBAAa,UAAU;AAAA,QACzB;AAEA,wBAAgB;AAChB,qBAAa;AACb,yBAAiB;AACjB,wBAAgB;AAChB,mBAAW,GAAG;AACd,sBAAc,GAAG;AACjB,sBAAc,GAAG;AACjB,uBAAe,GAAG;AAAA,MACpB;AAEA,UAAI,QAAQ;AACZ,mBAAa,UAAU,sBAAsB,OAAO;AAAA,IACtD;AAEA,UAAM,QAAQ,MAAM;AAClB,YAAM,EAAE,OAAO,OAAO,IAAI,UAAU,sBAAsB;AAC1D,oBAAc,UAAU,EAAE,OAAO,OAAO;AACxC,YAAM,OAAO,oBAAoB;AAEjC,aAAO,QAAQ,QAAQ;AACvB,aAAO,SAAS,SAAS;AACzB,aAAO,MAAM,QAAQ,GAAG,KAAK;AAC7B,aAAO,MAAM,SAAS,GAAG,MAAM;AAE/B,UAAI,UAAU,GAAG;AACf,yBAAiB,OAAO,MAAM;AAAA,MAChC,OAAO;AACL,sBAAc,OAAO,MAAM;AAAA,MAC7B;AAEA,mBAAa,UAAU,CAAC;AACxB,gBAAU,UAAU,CAAC;AACrB,qBAAe,QAAQ,MAAM;AAC7B,oBAAc,UAAU,CAAC;AACzB,mBAAa,UAAU,CAAC;AAAA,IAC1B;AAEA,UAAM,iBAAiB,IAAI,eAAe,MAAM;AAC9C,YAAM;AAAA,IACR,CAAC;AAED,mBAAe,QAAQ,SAAS;AAChC,UAAM;AACN,iBAAa,UAAU,sBAAsB,OAAO;AAEpD,WAAO,MAAM;AACX,qBAAe,WAAW;AAC1B,2BAAqB,aAAa,OAAO;AAAA,IAC3C;AAAA,EACF,GAAG,CAAC,OAAO,UAAU,WAAW,WAAW,UAAU,YAAY,YAAY,WAAW,WAAW,WAAW,aAAa,gBAAgB,kBAAkB,eAAe,iBAAiB,CAAC;AAE9L,SACE,4CAAC,SAAI,KAAK,cAAc,WAAW,0BAA0B,SAAS,IACpE,sDAAC,YAAO,KAAK,WAAW,WAAU,oBAAmB,GACvD;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Grid.tsx"],"sourcesContent":["export { Grid, type GridProps } from \"./Grid\";\nexport { Grid as default } from \"./Grid\";\n","'use client';\n\nimport { useEffect, useRef, useCallback } from 'react';\n\nexport interface GridProps {\n /** Grid shape: 4 for squares, 6 for hexagons */\n shape?: 4 | 6;\n /** Size of each cell in pixels */\n cellSize?: number;\n /** Color of the grid lines (use \"transparent\" to hide) */\n lineColor?: string;\n /** Width of the grid lines */\n lineWidth?: number;\n /** Additional CSS classes */\n className?: string;\n /** Enable animated light particles */\n animated?: boolean;\n /** Color of the light particles and trails */\n lightColor?: string;\n /** Speed of the light particles (1-5 recommended) */\n lightSpeed?: number;\n /** Minimum travel distance before particle dies */\n minTravel?: number;\n /** Maximum travel distance before particle dies */\n maxTravel?: number;\n /** Milliseconds between particle spawns */\n spawnRate?: number;\n /** Probability of particle splitting (0-1) */\n splitChance?: number;\n /** Speed at which trails fade (lower = slower fade) */\n trailFadeSpeed?: number;\n /** Enable spawning particles from the bottom as well as the top */\n bidirectional?: boolean;\n}\n\ninterface Node {\n x: number;\n y: number;\n key: string;\n}\n\ninterface Edge {\n from: Node;\n to: Node;\n key: string;\n}\n\ninterface Particle {\n id: number;\n currentNode: Node;\n targetNode: Node;\n progress: number;\n traveled: number;\n maxTravel: number;\n canSplit: boolean;\n dead: boolean;\n direction: 1 | -1;\n history: { x: number; y: number }[];\n}\n\ninterface Trail {\n fromX: number;\n fromY: number;\n toX: number;\n toY: number;\n progress: number;\n opacity: number;\n edgeKey: string;\n}\n\ninterface Explosion {\n x: number;\n y: number;\n radius: number;\n maxRadius: number;\n opacity: number;\n}\n\ninterface NodeGlow {\n x: number;\n y: number;\n opacity: number;\n}\n\nexport function Grid({\n shape = 4,\n cellSize = 40,\n lineColor = '#e5e7eb',\n lineWidth = 1,\n className = '',\n animated = false,\n lightColor = '#3b82f6',\n lightSpeed = 2,\n minTravel = 2,\n maxTravel = 6,\n spawnRate = 1000,\n splitChance = 0.3,\n trailFadeSpeed = 0.01,\n bidirectional = false,\n}: GridProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const nodesRef = useRef<Map<string, Node>>(new Map());\n const edgesRef = useRef<Edge[]>([]);\n const adjacencyRef = useRef<Map<string, Edge[]>>(new Map());\n const topNodesRef = useRef<Node[]>([]);\n const bottomNodesRef = useRef<Node[]>([]);\n const animationRef = useRef<number>(0);\n const particlesRef = useRef<Particle[]>([]);\n const trailsRef = useRef<Trail[]>([]);\n const lastSpawnRef = useRef<number>(0);\n const particleIdRef = useRef<number>(0);\n const dimensionsRef = useRef({ width: 0, height: 0 });\n const activeEdgesRef = useRef<Set<string>>(new Set());\n const explosionsRef = useRef<Explosion[]>([]);\n const nodeGlowsRef = useRef<NodeGlow[]>([]);\n const spawnFromTopRef = useRef<boolean>(true);\n\n const nodeKey = (x: number, y: number) =>\n `${Math.round(x * 100)},${Math.round(y * 100)}`;\n const edgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const getOrCreateNode = useCallback((x: number, y: number): Node => {\n const key = nodeKey(x, y);\n if (!nodesRef.current.has(key)) {\n nodesRef.current.set(key, { x, y, key });\n }\n return nodesRef.current.get(key)!;\n }, []);\n\n const addEdge = useCallback(\n (x1: number, y1: number, x2: number, y2: number) => {\n const from = getOrCreateNode(x1, y1);\n const to = getOrCreateNode(x2, y2);\n const key = edgeKey(from, to);\n\n const existingEdge = edgesRef.current.find((e) => e.key === key);\n if (existingEdge) return;\n\n const edge: Edge = { from, to, key };\n edgesRef.current.push(edge);\n\n if (!adjacencyRef.current.has(from.key)) {\n adjacencyRef.current.set(from.key, []);\n }\n if (!adjacencyRef.current.has(to.key)) {\n adjacencyRef.current.set(to.key, []);\n }\n adjacencyRef.current.get(from.key)!.push(edge);\n adjacencyRef.current.get(to.key)!.push(edge);\n },\n [getOrCreateNode]\n );\n\n const buildSquareGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const cols = Math.ceil(width / cellSize) + 1;\n const rows = Math.ceil(height / cellSize) + 1;\n\n for (let i = 0; i <= cols; i++) {\n for (let j = 0; j <= rows; j++) {\n const x = i * cellSize;\n const y = j * cellSize;\n if (i < cols) addEdge(x, y, x + cellSize, y);\n if (j < rows) addEdge(x, y, x, y + cellSize);\n }\n }\n\n const maxY = rows * cellSize;\n for (let i = 0; i <= cols; i++) {\n const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));\n if (topNode) topNodesRef.current.push(topNode);\n\n const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));\n if (bottomNode) bottomNodesRef.current.push(bottomNode);\n }\n },\n [cellSize, addEdge]\n );\n\n const buildHexGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const size = cellSize / 2;\n const hexWidth = Math.sqrt(3) * size;\n const vertSpacing = size * 1.5;\n\n const cols = Math.ceil(width / hexWidth) + 2;\n const rows = Math.ceil(height / vertSpacing) + 2;\n\n for (let row = -1; row < rows; row++) {\n for (let col = -1; col < cols; col++) {\n const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);\n const cy = row * vertSpacing;\n\n const vertices: { x: number; y: number }[] = [];\n for (let i = 0; i < 6; i++) {\n const angle = (Math.PI / 3) * i - Math.PI / 2;\n vertices.push({\n x: cx + size * Math.cos(angle),\n y: cy + size * Math.sin(angle),\n });\n }\n\n for (let i = 0; i < 6; i++) {\n const v1 = vertices[i];\n const v2 = vertices[(i + 1) % 6];\n addEdge(v1.x, v1.y, v2.x, v2.y);\n }\n }\n }\n\n const allNodes = Array.from(nodesRef.current.values());\n\n const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);\n\n topNodesRef.current = visibleNodes\n .filter((n) => n.y <= size * 2)\n .sort((a, b) => a.x - b.x);\n\n bottomNodesRef.current = visibleNodes\n .filter((n) => n.y >= height - size * 2)\n .sort((a, b) => a.x - b.x);\n },\n [cellSize, addEdge]\n );\n\n const getConnectedNodes = useCallback(\n (node: Node, excludeNode?: Node): Node[] => {\n const edges = adjacencyRef.current.get(node.key) || [];\n return edges\n .map((e) => (e.from.key === node.key ? e.to : e.from))\n .filter((n) => !excludeNode || n.key !== excludeNode.key);\n },\n []\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container) return;\n\n let dpr = 1;\n\n const drawGrid = (\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number\n ) => {\n ctx.clearRect(0, 0, width, height);\n ctx.strokeStyle = lineColor;\n ctx.lineWidth = lineWidth;\n ctx.beginPath();\n\n for (const edge of edgesRef.current) {\n ctx.moveTo(edge.from.x, edge.from.y);\n ctx.lineTo(edge.to.x, edge.to.y);\n }\n\n ctx.stroke();\n };\n\n const drawTrails = (ctx: CanvasRenderingContext2D) => {\n for (const trail of trailsRef.current) {\n if (trail.opacity <= 0) continue;\n\n const alpha = Math.floor(trail.opacity * 0.08 * 255)\n .toString(16)\n .padStart(2, '0');\n\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 1;\n ctx.lineCap = 'round';\n ctx.beginPath();\n ctx.moveTo(trail.fromX, trail.fromY);\n\n if (trail.progress < 1) {\n const endX = trail.fromX + (trail.toX - trail.fromX) * trail.progress;\n const endY = trail.fromY + (trail.toY - trail.fromY) * trail.progress;\n ctx.lineTo(endX, endY);\n } else {\n ctx.lineTo(trail.toX, trail.toY);\n }\n ctx.stroke();\n\n ctx.shadowColor = lightColor;\n ctx.shadowBlur = 2;\n ctx.stroke();\n ctx.shadowBlur = 0;\n }\n };\n\n const drawParticles = (ctx: CanvasRenderingContext2D) => {\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n const history = particle.history;\n if (history.length === 0) continue;\n\n const head = history[history.length - 1];\n\n if (history.length >= 2) {\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.strokeStyle = lightColor + '40';\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(history[0].x, history[0].y);\n\n for (let i = 1; i < history.length; i++) {\n ctx.lineTo(history[i].x, history[i].y);\n }\n ctx.stroke();\n }\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n ctx.arc(head.x, head.y, 3, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const drawExplosions = (ctx: CanvasRenderingContext2D) => {\n for (const explosion of explosionsRef.current) {\n if (explosion.opacity <= 0) continue;\n\n const alpha = Math.floor(explosion.opacity * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);\n ctx.stroke();\n }\n };\n\n const updateExplosions = () => {\n for (const explosion of explosionsRef.current) {\n explosion.radius += 0.5;\n explosion.opacity -= 0.08;\n }\n explosionsRef.current = explosionsRef.current.filter(\n (e) => e.opacity > 0\n );\n };\n\n const createExplosion = (x: number, y: number) => {\n explosionsRef.current.push({\n x,\n y,\n radius: 2,\n maxRadius: 8,\n opacity: 1,\n });\n };\n\n const createNodeGlow = (x: number, y: number) => {\n nodeGlowsRef.current.push({\n x,\n y,\n opacity: 1,\n });\n };\n\n const drawNodeGlows = (ctx: CanvasRenderingContext2D) => {\n for (const glow of nodeGlowsRef.current) {\n if (glow.opacity <= 0) continue;\n\n const alpha = Math.floor(glow.opacity * 0.4 * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.fillStyle = lightColor + alpha;\n ctx.beginPath();\n ctx.arc(glow.x, glow.y, 2, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const updateNodeGlows = () => {\n for (const glow of nodeGlowsRef.current) {\n glow.opacity -= trailFadeSpeed * 3;\n }\n nodeGlowsRef.current = nodeGlowsRef.current.filter((g) => g.opacity > 0);\n };\n\n const getRandomTravel = () => {\n return (\n Math.floor(Math.random() * (maxTravel - minTravel + 1)) + minTravel\n );\n };\n\n const makeEdgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const trySpawn = (fromTop: boolean): boolean => {\n const sourceNodes = fromTop\n ? topNodesRef.current\n : bottomNodesRef.current;\n const direction: 1 | -1 = fromTop ? 1 : -1;\n\n if (sourceNodes.length === 0) return false;\n\n const startNode =\n sourceNodes[Math.floor(Math.random() * sourceNodes.length)];\n const connectedNodes = getConnectedNodes(startNode);\n\n const preferredNodes = connectedNodes.filter((n) =>\n fromTop ? n.y > startNode.y : n.y < startNode.y\n );\n const targetNodes =\n preferredNodes.length > 0 ? preferredNodes : connectedNodes;\n\n const availableNodes = targetNodes.filter((n) => {\n const key = makeEdgeKey(startNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) return false;\n\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const edgeKeyStr = makeEdgeKey(startNode, targetNode);\n activeEdgesRef.current.add(edgeKeyStr);\n\n particlesRef.current.push({\n id: particleIdRef.current++,\n currentNode: startNode,\n targetNode,\n progress: 0,\n traveled: 0,\n maxTravel: getRandomTravel(),\n canSplit: true,\n dead: false,\n direction,\n history: [{ x: startNode.x, y: startNode.y }],\n });\n\n trailsRef.current.push({\n fromX: startNode.x,\n fromY: startNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKeyStr,\n });\n\n return true;\n };\n\n const spawnParticle = () => {\n if (bidirectional) {\n const fromTop = spawnFromTopRef.current;\n spawnFromTopRef.current = !spawnFromTopRef.current;\n\n if (!trySpawn(fromTop)) {\n trySpawn(!fromTop);\n }\n } else {\n trySpawn(true);\n }\n };\n\n const updateParticles = () => {\n const newParticles: Particle[] = [];\n const maxHistoryLength = 10;\n\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n particle.progress += lightSpeed * 0.02;\n\n const currentX =\n particle.currentNode.x +\n (particle.targetNode.x - particle.currentNode.x) * particle.progress;\n const currentY =\n particle.currentNode.y +\n (particle.targetNode.y - particle.currentNode.y) * particle.progress;\n particle.history.push({ x: currentX, y: currentY });\n if (particle.history.length > maxHistoryLength) {\n particle.history.shift();\n }\n\n const trailIndex = trailsRef.current.findIndex(\n (t) =>\n t.fromX === particle.currentNode.x &&\n t.fromY === particle.currentNode.y &&\n t.toX === particle.targetNode.x &&\n t.toY === particle.targetNode.y &&\n t.progress < 1\n );\n if (trailIndex !== -1) {\n trailsRef.current[trailIndex].progress = Math.min(\n particle.progress,\n 1\n );\n }\n\n if (particle.progress >= 1) {\n createNodeGlow(particle.targetNode.x, particle.targetNode.y);\n particle.traveled++;\n\n if (particle.traveled >= particle.maxTravel) {\n const x = particle.targetNode.x;\n const y = particle.targetNode.y;\n createExplosion(x, y);\n particle.dead = true;\n continue;\n }\n\n const currentNode = particle.targetNode;\n const connectedNodes = getConnectedNodes(\n currentNode,\n particle.currentNode\n );\n\n const availableNodes = connectedNodes.filter((n) => {\n const key = makeEdgeKey(currentNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) {\n createExplosion(currentNode.x, currentNode.y);\n particle.dead = true;\n continue;\n }\n\n const shouldSplit =\n particle.canSplit &&\n Math.random() < splitChance &&\n availableNodes.length >= 2;\n\n if (shouldSplit) {\n const shuffled = [...availableNodes].sort(\n () => Math.random() - 0.5\n );\n const target1 = shuffled[0];\n const target2 = shuffled[1];\n\n const edgeKey1 = makeEdgeKey(currentNode, target1);\n const edgeKey2 = makeEdgeKey(currentNode, target2);\n activeEdgesRef.current.add(edgeKey1);\n activeEdgesRef.current.add(edgeKey2);\n\n particle.currentNode = currentNode;\n particle.targetNode = target1;\n particle.progress = 0;\n particle.canSplit = false;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target1.x,\n toY: target1.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey1,\n });\n\n newParticles.push({\n id: particleIdRef.current++,\n currentNode: currentNode,\n targetNode: target2,\n progress: 0,\n traveled: particle.traveled,\n maxTravel: particle.maxTravel,\n canSplit: false,\n dead: false,\n direction: particle.direction,\n history: [{ x: currentNode.x, y: currentNode.y }],\n });\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target2.x,\n toY: target2.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey2,\n });\n } else {\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const newEdgeKey = makeEdgeKey(currentNode, targetNode);\n activeEdgesRef.current.add(newEdgeKey);\n\n particle.currentNode = currentNode;\n particle.targetNode = targetNode;\n particle.progress = 0;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: newEdgeKey,\n });\n }\n }\n }\n\n particlesRef.current.push(...newParticles);\n particlesRef.current = particlesRef.current.filter((p) => !p.dead);\n };\n\n const updateTrails = () => {\n for (const trail of trailsRef.current) {\n if (trail.progress >= 1) {\n trail.opacity -= trailFadeSpeed;\n }\n }\n const fadedTrails = trailsRef.current.filter((t) => t.opacity <= 0);\n for (const trail of fadedTrails) {\n activeEdgesRef.current.delete(trail.edgeKey);\n }\n trailsRef.current = trailsRef.current.filter((t) => t.opacity > 0);\n };\n\n const animate = (timestamp: number) => {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const { width, height } = dimensionsRef.current;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n drawGrid(ctx, width, height);\n\n if (animated) {\n if (timestamp - lastSpawnRef.current > spawnRate) {\n spawnParticle();\n lastSpawnRef.current = timestamp;\n }\n\n updateParticles();\n updateTrails();\n updateExplosions();\n updateNodeGlows();\n drawTrails(ctx);\n drawNodeGlows(ctx);\n drawParticles(ctx);\n drawExplosions(ctx);\n }\n\n ctx.restore();\n animationRef.current = requestAnimationFrame(animate);\n };\n\n const setup = () => {\n const { width, height } = container.getBoundingClientRect();\n dimensionsRef.current = { width, height };\n dpr = window.devicePixelRatio || 1;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n if (shape === 4) {\n buildSquareGraph(width, height);\n } else {\n buildHexGraph(width, height);\n }\n\n particlesRef.current = [];\n trailsRef.current = [];\n activeEdgesRef.current.clear();\n explosionsRef.current = [];\n nodeGlowsRef.current = [];\n };\n\n const resizeObserver = new ResizeObserver(() => {\n setup();\n });\n\n resizeObserver.observe(container);\n setup();\n animationRef.current = requestAnimationFrame(animate);\n\n return () => {\n resizeObserver.disconnect();\n cancelAnimationFrame(animationRef.current);\n };\n }, [\n shape,\n cellSize,\n lineColor,\n lineWidth,\n animated,\n lightColor,\n lightSpeed,\n minTravel,\n maxTravel,\n spawnRate,\n splitChance,\n trailFadeSpeed,\n bidirectional,\n buildSquareGraph,\n buildHexGraph,\n getConnectedNodes,\n ]);\n\n return (\n <div ref={containerRef} className={`relative w-full h-full ${className}`}>\n <canvas ref={canvasRef} className=\"absolute inset-0\" />\n </div>\n );\n}\n\nexport default Grid;\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -16,7 +16,8 @@ function Grid({
|
|
|
16
16
|
maxTravel = 6,
|
|
17
17
|
spawnRate = 1e3,
|
|
18
18
|
splitChance = 0.3,
|
|
19
|
-
trailFadeSpeed = 0.01
|
|
19
|
+
trailFadeSpeed = 0.01,
|
|
20
|
+
bidirectional = false
|
|
20
21
|
}) {
|
|
21
22
|
const canvasRef = useRef(null);
|
|
22
23
|
const containerRef = useRef(null);
|
|
@@ -24,6 +25,7 @@ function Grid({
|
|
|
24
25
|
const edgesRef = useRef([]);
|
|
25
26
|
const adjacencyRef = useRef(/* @__PURE__ */ new Map());
|
|
26
27
|
const topNodesRef = useRef([]);
|
|
28
|
+
const bottomNodesRef = useRef([]);
|
|
27
29
|
const animationRef = useRef(0);
|
|
28
30
|
const particlesRef = useRef([]);
|
|
29
31
|
const trailsRef = useRef([]);
|
|
@@ -33,6 +35,7 @@ function Grid({
|
|
|
33
35
|
const activeEdgesRef = useRef(/* @__PURE__ */ new Set());
|
|
34
36
|
const explosionsRef = useRef([]);
|
|
35
37
|
const nodeGlowsRef = useRef([]);
|
|
38
|
+
const spawnFromTopRef = useRef(true);
|
|
36
39
|
const nodeKey = (x, y) => `${Math.round(x * 100)},${Math.round(y * 100)}`;
|
|
37
40
|
const edgeKey = (n1, n2) => {
|
|
38
41
|
const keys = [n1.key, n2.key].sort();
|
|
@@ -45,79 +48,98 @@ function Grid({
|
|
|
45
48
|
}
|
|
46
49
|
return nodesRef.current.get(key);
|
|
47
50
|
}, []);
|
|
48
|
-
const addEdge = useCallback(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
adjacencyRef.current.
|
|
58
|
-
|
|
59
|
-
if (!adjacencyRef.current.has(to.key)) {
|
|
60
|
-
adjacencyRef.current.set(to.key, []);
|
|
61
|
-
}
|
|
62
|
-
adjacencyRef.current.get(from.key).push(edge);
|
|
63
|
-
adjacencyRef.current.get(to.key).push(edge);
|
|
64
|
-
}, [getOrCreateNode]);
|
|
65
|
-
const buildSquareGraph = useCallback((width, height) => {
|
|
66
|
-
nodesRef.current.clear();
|
|
67
|
-
edgesRef.current = [];
|
|
68
|
-
adjacencyRef.current.clear();
|
|
69
|
-
topNodesRef.current = [];
|
|
70
|
-
const cols = Math.ceil(width / cellSize) + 1;
|
|
71
|
-
const rows = Math.ceil(height / cellSize) + 1;
|
|
72
|
-
for (let i = 0; i <= cols; i++) {
|
|
73
|
-
for (let j = 0; j <= rows; j++) {
|
|
74
|
-
const x = i * cellSize;
|
|
75
|
-
const y = j * cellSize;
|
|
76
|
-
if (i < cols) addEdge(x, y, x + cellSize, y);
|
|
77
|
-
if (j < rows) addEdge(x, y, x, y + cellSize);
|
|
51
|
+
const addEdge = useCallback(
|
|
52
|
+
(x1, y1, x2, y2) => {
|
|
53
|
+
const from = getOrCreateNode(x1, y1);
|
|
54
|
+
const to = getOrCreateNode(x2, y2);
|
|
55
|
+
const key = edgeKey(from, to);
|
|
56
|
+
const existingEdge = edgesRef.current.find((e) => e.key === key);
|
|
57
|
+
if (existingEdge) return;
|
|
58
|
+
const edge = { from, to, key };
|
|
59
|
+
edgesRef.current.push(edge);
|
|
60
|
+
if (!adjacencyRef.current.has(from.key)) {
|
|
61
|
+
adjacencyRef.current.set(from.key, []);
|
|
78
62
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
for (let
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
vertices.push({
|
|
103
|
-
x: cx + size * Math.cos(angle),
|
|
104
|
-
y: cy + size * Math.sin(angle)
|
|
105
|
-
});
|
|
63
|
+
if (!adjacencyRef.current.has(to.key)) {
|
|
64
|
+
adjacencyRef.current.set(to.key, []);
|
|
65
|
+
}
|
|
66
|
+
adjacencyRef.current.get(from.key).push(edge);
|
|
67
|
+
adjacencyRef.current.get(to.key).push(edge);
|
|
68
|
+
},
|
|
69
|
+
[getOrCreateNode]
|
|
70
|
+
);
|
|
71
|
+
const buildSquareGraph = useCallback(
|
|
72
|
+
(width, height) => {
|
|
73
|
+
nodesRef.current.clear();
|
|
74
|
+
edgesRef.current = [];
|
|
75
|
+
adjacencyRef.current.clear();
|
|
76
|
+
topNodesRef.current = [];
|
|
77
|
+
bottomNodesRef.current = [];
|
|
78
|
+
const cols = Math.ceil(width / cellSize) + 1;
|
|
79
|
+
const rows = Math.ceil(height / cellSize) + 1;
|
|
80
|
+
for (let i = 0; i <= cols; i++) {
|
|
81
|
+
for (let j = 0; j <= rows; j++) {
|
|
82
|
+
const x = i * cellSize;
|
|
83
|
+
const y = j * cellSize;
|
|
84
|
+
if (i < cols) addEdge(x, y, x + cellSize, y);
|
|
85
|
+
if (j < rows) addEdge(x, y, x, y + cellSize);
|
|
106
86
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
87
|
+
}
|
|
88
|
+
const maxY = rows * cellSize;
|
|
89
|
+
for (let i = 0; i <= cols; i++) {
|
|
90
|
+
const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));
|
|
91
|
+
if (topNode) topNodesRef.current.push(topNode);
|
|
92
|
+
const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));
|
|
93
|
+
if (bottomNode) bottomNodesRef.current.push(bottomNode);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[cellSize, addEdge]
|
|
97
|
+
);
|
|
98
|
+
const buildHexGraph = useCallback(
|
|
99
|
+
(width, height) => {
|
|
100
|
+
nodesRef.current.clear();
|
|
101
|
+
edgesRef.current = [];
|
|
102
|
+
adjacencyRef.current.clear();
|
|
103
|
+
topNodesRef.current = [];
|
|
104
|
+
bottomNodesRef.current = [];
|
|
105
|
+
const size = cellSize / 2;
|
|
106
|
+
const hexWidth = Math.sqrt(3) * size;
|
|
107
|
+
const vertSpacing = size * 1.5;
|
|
108
|
+
const cols = Math.ceil(width / hexWidth) + 2;
|
|
109
|
+
const rows = Math.ceil(height / vertSpacing) + 2;
|
|
110
|
+
for (let row = -1; row < rows; row++) {
|
|
111
|
+
for (let col = -1; col < cols; col++) {
|
|
112
|
+
const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);
|
|
113
|
+
const cy = row * vertSpacing;
|
|
114
|
+
const vertices = [];
|
|
115
|
+
for (let i = 0; i < 6; i++) {
|
|
116
|
+
const angle = Math.PI / 3 * i - Math.PI / 2;
|
|
117
|
+
vertices.push({
|
|
118
|
+
x: cx + size * Math.cos(angle),
|
|
119
|
+
y: cy + size * Math.sin(angle)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
for (let i = 0; i < 6; i++) {
|
|
123
|
+
const v1 = vertices[i];
|
|
124
|
+
const v2 = vertices[(i + 1) % 6];
|
|
125
|
+
addEdge(v1.x, v1.y, v2.x, v2.y);
|
|
126
|
+
}
|
|
111
127
|
}
|
|
112
128
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
const allNodes = Array.from(nodesRef.current.values());
|
|
130
|
+
const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);
|
|
131
|
+
topNodesRef.current = visibleNodes.filter((n) => n.y <= size * 2).sort((a, b) => a.x - b.x);
|
|
132
|
+
bottomNodesRef.current = visibleNodes.filter((n) => n.y >= height - size * 2).sort((a, b) => a.x - b.x);
|
|
133
|
+
},
|
|
134
|
+
[cellSize, addEdge]
|
|
135
|
+
);
|
|
136
|
+
const getConnectedNodes = useCallback(
|
|
137
|
+
(node, excludeNode) => {
|
|
138
|
+
const edges = adjacencyRef.current.get(node.key) || [];
|
|
139
|
+
return edges.map((e) => e.from.key === node.key ? e.to : e.from).filter((n) => !excludeNode || n.key !== excludeNode.key);
|
|
140
|
+
},
|
|
141
|
+
[]
|
|
142
|
+
);
|
|
121
143
|
useEffect(() => {
|
|
122
144
|
const canvas = canvasRef.current;
|
|
123
145
|
const container = containerRef.current;
|
|
@@ -160,11 +182,24 @@ function Grid({
|
|
|
160
182
|
const drawParticles = (ctx) => {
|
|
161
183
|
for (const particle of particlesRef.current) {
|
|
162
184
|
if (particle.dead) continue;
|
|
163
|
-
const
|
|
164
|
-
|
|
185
|
+
const history = particle.history;
|
|
186
|
+
if (history.length === 0) continue;
|
|
187
|
+
const head = history[history.length - 1];
|
|
188
|
+
if (history.length >= 2) {
|
|
189
|
+
ctx.lineCap = "round";
|
|
190
|
+
ctx.lineJoin = "round";
|
|
191
|
+
ctx.strokeStyle = lightColor + "40";
|
|
192
|
+
ctx.lineWidth = 2;
|
|
193
|
+
ctx.beginPath();
|
|
194
|
+
ctx.moveTo(history[0].x, history[0].y);
|
|
195
|
+
for (let i = 1; i < history.length; i++) {
|
|
196
|
+
ctx.lineTo(history[i].x, history[i].y);
|
|
197
|
+
}
|
|
198
|
+
ctx.stroke();
|
|
199
|
+
}
|
|
165
200
|
ctx.fillStyle = lightColor;
|
|
166
201
|
ctx.beginPath();
|
|
167
|
-
ctx.arc(x, y, 3, 0, Math.PI * 2);
|
|
202
|
+
ctx.arc(head.x, head.y, 3, 0, Math.PI * 2);
|
|
168
203
|
ctx.fill();
|
|
169
204
|
}
|
|
170
205
|
};
|
|
@@ -184,7 +219,9 @@ function Grid({
|
|
|
184
219
|
explosion.radius += 0.5;
|
|
185
220
|
explosion.opacity -= 0.08;
|
|
186
221
|
}
|
|
187
|
-
explosionsRef.current = explosionsRef.current.filter(
|
|
222
|
+
explosionsRef.current = explosionsRef.current.filter(
|
|
223
|
+
(e) => e.opacity > 0
|
|
224
|
+
);
|
|
188
225
|
};
|
|
189
226
|
const createExplosion = (x, y) => {
|
|
190
227
|
explosionsRef.current.push({
|
|
@@ -225,17 +262,21 @@ function Grid({
|
|
|
225
262
|
const keys = [n1.key, n2.key].sort();
|
|
226
263
|
return `${keys[0]}-${keys[1]}`;
|
|
227
264
|
};
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
const
|
|
265
|
+
const trySpawn = (fromTop) => {
|
|
266
|
+
const sourceNodes = fromTop ? topNodesRef.current : bottomNodesRef.current;
|
|
267
|
+
const direction = fromTop ? 1 : -1;
|
|
268
|
+
if (sourceNodes.length === 0) return false;
|
|
269
|
+
const startNode = sourceNodes[Math.floor(Math.random() * sourceNodes.length)];
|
|
231
270
|
const connectedNodes = getConnectedNodes(startNode);
|
|
232
|
-
const
|
|
233
|
-
|
|
271
|
+
const preferredNodes = connectedNodes.filter(
|
|
272
|
+
(n) => fromTop ? n.y > startNode.y : n.y < startNode.y
|
|
273
|
+
);
|
|
274
|
+
const targetNodes = preferredNodes.length > 0 ? preferredNodes : connectedNodes;
|
|
234
275
|
const availableNodes = targetNodes.filter((n) => {
|
|
235
276
|
const key = makeEdgeKey(startNode, n);
|
|
236
277
|
return !activeEdgesRef.current.has(key);
|
|
237
278
|
});
|
|
238
|
-
if (availableNodes.length === 0) return;
|
|
279
|
+
if (availableNodes.length === 0) return false;
|
|
239
280
|
const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];
|
|
240
281
|
const edgeKeyStr = makeEdgeKey(startNode, targetNode);
|
|
241
282
|
activeEdgesRef.current.add(edgeKeyStr);
|
|
@@ -247,7 +288,9 @@ function Grid({
|
|
|
247
288
|
traveled: 0,
|
|
248
289
|
maxTravel: getRandomTravel(),
|
|
249
290
|
canSplit: true,
|
|
250
|
-
dead: false
|
|
291
|
+
dead: false,
|
|
292
|
+
direction,
|
|
293
|
+
history: [{ x: startNode.x, y: startNode.y }]
|
|
251
294
|
});
|
|
252
295
|
trailsRef.current.push({
|
|
253
296
|
fromX: startNode.x,
|
|
@@ -258,17 +301,39 @@ function Grid({
|
|
|
258
301
|
opacity: 1,
|
|
259
302
|
edgeKey: edgeKeyStr
|
|
260
303
|
});
|
|
304
|
+
return true;
|
|
305
|
+
};
|
|
306
|
+
const spawnParticle = () => {
|
|
307
|
+
if (bidirectional) {
|
|
308
|
+
const fromTop = spawnFromTopRef.current;
|
|
309
|
+
spawnFromTopRef.current = !spawnFromTopRef.current;
|
|
310
|
+
if (!trySpawn(fromTop)) {
|
|
311
|
+
trySpawn(!fromTop);
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
trySpawn(true);
|
|
315
|
+
}
|
|
261
316
|
};
|
|
262
317
|
const updateParticles = () => {
|
|
263
318
|
const newParticles = [];
|
|
319
|
+
const maxHistoryLength = 10;
|
|
264
320
|
for (const particle of particlesRef.current) {
|
|
265
321
|
if (particle.dead) continue;
|
|
266
322
|
particle.progress += lightSpeed * 0.02;
|
|
323
|
+
const currentX = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;
|
|
324
|
+
const currentY = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;
|
|
325
|
+
particle.history.push({ x: currentX, y: currentY });
|
|
326
|
+
if (particle.history.length > maxHistoryLength) {
|
|
327
|
+
particle.history.shift();
|
|
328
|
+
}
|
|
267
329
|
const trailIndex = trailsRef.current.findIndex(
|
|
268
330
|
(t) => t.fromX === particle.currentNode.x && t.fromY === particle.currentNode.y && t.toX === particle.targetNode.x && t.toY === particle.targetNode.y && t.progress < 1
|
|
269
331
|
);
|
|
270
332
|
if (trailIndex !== -1) {
|
|
271
|
-
trailsRef.current[trailIndex].progress = Math.min(
|
|
333
|
+
trailsRef.current[trailIndex].progress = Math.min(
|
|
334
|
+
particle.progress,
|
|
335
|
+
1
|
|
336
|
+
);
|
|
272
337
|
}
|
|
273
338
|
if (particle.progress >= 1) {
|
|
274
339
|
createNodeGlow(particle.targetNode.x, particle.targetNode.y);
|
|
@@ -281,7 +346,10 @@ function Grid({
|
|
|
281
346
|
continue;
|
|
282
347
|
}
|
|
283
348
|
const currentNode = particle.targetNode;
|
|
284
|
-
const connectedNodes = getConnectedNodes(
|
|
349
|
+
const connectedNodes = getConnectedNodes(
|
|
350
|
+
currentNode,
|
|
351
|
+
particle.currentNode
|
|
352
|
+
);
|
|
285
353
|
const availableNodes = connectedNodes.filter((n) => {
|
|
286
354
|
const key = makeEdgeKey(currentNode, n);
|
|
287
355
|
return !activeEdgesRef.current.has(key);
|
|
@@ -293,7 +361,9 @@ function Grid({
|
|
|
293
361
|
}
|
|
294
362
|
const shouldSplit = particle.canSplit && Math.random() < splitChance && availableNodes.length >= 2;
|
|
295
363
|
if (shouldSplit) {
|
|
296
|
-
const shuffled = [...availableNodes].sort(
|
|
364
|
+
const shuffled = [...availableNodes].sort(
|
|
365
|
+
() => Math.random() - 0.5
|
|
366
|
+
);
|
|
297
367
|
const target1 = shuffled[0];
|
|
298
368
|
const target2 = shuffled[1];
|
|
299
369
|
const edgeKey1 = makeEdgeKey(currentNode, target1);
|
|
@@ -321,7 +391,9 @@ function Grid({
|
|
|
321
391
|
traveled: particle.traveled,
|
|
322
392
|
maxTravel: particle.maxTravel,
|
|
323
393
|
canSplit: false,
|
|
324
|
-
dead: false
|
|
394
|
+
dead: false,
|
|
395
|
+
direction: particle.direction,
|
|
396
|
+
history: [{ x: currentNode.x, y: currentNode.y }]
|
|
325
397
|
});
|
|
326
398
|
trailsRef.current.push({
|
|
327
399
|
fromX: currentNode.x,
|
|
@@ -419,7 +491,24 @@ function Grid({
|
|
|
419
491
|
resizeObserver.disconnect();
|
|
420
492
|
cancelAnimationFrame(animationRef.current);
|
|
421
493
|
};
|
|
422
|
-
}, [
|
|
494
|
+
}, [
|
|
495
|
+
shape,
|
|
496
|
+
cellSize,
|
|
497
|
+
lineColor,
|
|
498
|
+
lineWidth,
|
|
499
|
+
animated,
|
|
500
|
+
lightColor,
|
|
501
|
+
lightSpeed,
|
|
502
|
+
minTravel,
|
|
503
|
+
maxTravel,
|
|
504
|
+
spawnRate,
|
|
505
|
+
splitChance,
|
|
506
|
+
trailFadeSpeed,
|
|
507
|
+
bidirectional,
|
|
508
|
+
buildSquareGraph,
|
|
509
|
+
buildHexGraph,
|
|
510
|
+
getConnectedNodes
|
|
511
|
+
]);
|
|
423
512
|
return /* @__PURE__ */ jsx("div", { ref: containerRef, className: `relative w-full h-full ${className}`, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "absolute inset-0" }) });
|
|
424
513
|
}
|
|
425
514
|
export {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Grid.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useCallback } from \"react\";\n\nexport interface GridProps {\n /** Grid shape: 4 for squares, 6 for hexagons */\n shape?: 4 | 6;\n /** Size of each cell in pixels */\n cellSize?: number;\n /** Color of the grid lines (use \"transparent\" to hide) */\n lineColor?: string;\n /** Width of the grid lines */\n lineWidth?: number;\n /** Additional CSS classes */\n className?: string;\n /** Enable animated light particles */\n animated?: boolean;\n /** Color of the light particles and trails */\n lightColor?: string;\n /** Speed of the light particles (1-5 recommended) */\n lightSpeed?: number;\n /** Minimum travel distance before particle dies */\n minTravel?: number;\n /** Maximum travel distance before particle dies */\n maxTravel?: number;\n /** Milliseconds between particle spawns */\n spawnRate?: number;\n /** Probability of particle splitting (0-1) */\n splitChance?: number;\n /** Speed at which trails fade (lower = slower fade) */\n trailFadeSpeed?: number;\n}\n\ninterface Node {\n x: number;\n y: number;\n key: string;\n}\n\ninterface Edge {\n from: Node;\n to: Node;\n key: string;\n}\n\ninterface Particle {\n id: number;\n currentNode: Node;\n targetNode: Node;\n progress: number;\n traveled: number;\n maxTravel: number;\n canSplit: boolean;\n dead: boolean;\n}\n\ninterface Trail {\n fromX: number;\n fromY: number;\n toX: number;\n toY: number;\n progress: number;\n opacity: number;\n edgeKey: string;\n}\n\ninterface Explosion {\n x: number;\n y: number;\n radius: number;\n maxRadius: number;\n opacity: number;\n}\n\ninterface NodeGlow {\n x: number;\n y: number;\n opacity: number;\n}\n\nexport function Grid({\n shape = 4,\n cellSize = 40,\n lineColor = \"#e5e7eb\",\n lineWidth = 1,\n className = \"\",\n animated = false,\n lightColor = \"#3b82f6\",\n lightSpeed = 2,\n minTravel = 2,\n maxTravel = 6,\n spawnRate = 1000,\n splitChance = 0.3,\n trailFadeSpeed = 0.01,\n}: GridProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const nodesRef = useRef<Map<string, Node>>(new Map());\n const edgesRef = useRef<Edge[]>([]);\n const adjacencyRef = useRef<Map<string, Edge[]>>(new Map());\n const topNodesRef = useRef<Node[]>([]);\n const animationRef = useRef<number>(0);\n const particlesRef = useRef<Particle[]>([]);\n const trailsRef = useRef<Trail[]>([]);\n const lastSpawnRef = useRef<number>(0);\n const particleIdRef = useRef<number>(0);\n const dimensionsRef = useRef({ width: 0, height: 0 });\n const activeEdgesRef = useRef<Set<string>>(new Set());\n const explosionsRef = useRef<Explosion[]>([]);\n const nodeGlowsRef = useRef<NodeGlow[]>([]);\n\n const nodeKey = (x: number, y: number) => `${Math.round(x * 100)},${Math.round(y * 100)}`;\n const edgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const getOrCreateNode = useCallback((x: number, y: number): Node => {\n const key = nodeKey(x, y);\n if (!nodesRef.current.has(key)) {\n nodesRef.current.set(key, { x, y, key });\n }\n return nodesRef.current.get(key)!;\n }, []);\n\n const addEdge = useCallback((x1: number, y1: number, x2: number, y2: number) => {\n const from = getOrCreateNode(x1, y1);\n const to = getOrCreateNode(x2, y2);\n const key = edgeKey(from, to);\n\n const existingEdge = edgesRef.current.find(e => e.key === key);\n if (existingEdge) return;\n\n const edge: Edge = { from, to, key };\n edgesRef.current.push(edge);\n\n if (!adjacencyRef.current.has(from.key)) {\n adjacencyRef.current.set(from.key, []);\n }\n if (!adjacencyRef.current.has(to.key)) {\n adjacencyRef.current.set(to.key, []);\n }\n adjacencyRef.current.get(from.key)!.push(edge);\n adjacencyRef.current.get(to.key)!.push(edge);\n }, [getOrCreateNode]);\n\n const buildSquareGraph = useCallback((width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n\n const cols = Math.ceil(width / cellSize) + 1;\n const rows = Math.ceil(height / cellSize) + 1;\n\n for (let i = 0; i <= cols; i++) {\n for (let j = 0; j <= rows; j++) {\n const x = i * cellSize;\n const y = j * cellSize;\n if (i < cols) addEdge(x, y, x + cellSize, y);\n if (j < rows) addEdge(x, y, x, y + cellSize);\n }\n }\n\n for (let i = 0; i <= cols; i++) {\n const node = nodesRef.current.get(nodeKey(i * cellSize, 0));\n if (node) topNodesRef.current.push(node);\n }\n }, [cellSize, addEdge]);\n\n const buildHexGraph = useCallback((width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n\n const size = cellSize / 2;\n const hexWidth = Math.sqrt(3) * size;\n const vertSpacing = size * 1.5;\n\n const cols = Math.ceil(width / hexWidth) + 2;\n const rows = Math.ceil(height / vertSpacing) + 2;\n\n for (let row = -1; row < rows; row++) {\n for (let col = -1; col < cols; col++) {\n const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);\n const cy = row * vertSpacing;\n\n const vertices: { x: number; y: number }[] = [];\n for (let i = 0; i < 6; i++) {\n const angle = (Math.PI / 3) * i - Math.PI / 2;\n vertices.push({\n x: cx + size * Math.cos(angle),\n y: cy + size * Math.sin(angle),\n });\n }\n\n for (let i = 0; i < 6; i++) {\n const v1 = vertices[i];\n const v2 = vertices[(i + 1) % 6];\n addEdge(v1.x, v1.y, v2.x, v2.y);\n }\n }\n }\n\n const sortedNodes = Array.from(nodesRef.current.values())\n .filter(n => n.y >= 0 && n.y <= size)\n .sort((a, b) => a.x - b.x);\n topNodesRef.current = sortedNodes;\n }, [cellSize, addEdge]);\n\n const getConnectedNodes = useCallback((node: Node, excludeNode?: Node): Node[] => {\n const edges = adjacencyRef.current.get(node.key) || [];\n return edges\n .map(e => (e.from.key === node.key ? e.to : e.from))\n .filter(n => !excludeNode || n.key !== excludeNode.key);\n }, []);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container) return;\n\n let dpr = 1;\n\n const drawGrid = (ctx: CanvasRenderingContext2D, width: number, height: number) => {\n ctx.clearRect(0, 0, width, height);\n ctx.strokeStyle = lineColor;\n ctx.lineWidth = lineWidth;\n ctx.beginPath();\n\n for (const edge of edgesRef.current) {\n ctx.moveTo(edge.from.x, edge.from.y);\n ctx.lineTo(edge.to.x, edge.to.y);\n }\n\n ctx.stroke();\n };\n\n const drawTrails = (ctx: CanvasRenderingContext2D) => {\n for (const trail of trailsRef.current) {\n if (trail.opacity <= 0) continue;\n\n const alpha = Math.floor(trail.opacity * 0.08 * 255).toString(16).padStart(2, '0');\n\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 1;\n ctx.lineCap = \"round\";\n ctx.beginPath();\n ctx.moveTo(trail.fromX, trail.fromY);\n\n if (trail.progress < 1) {\n const endX = trail.fromX + (trail.toX - trail.fromX) * trail.progress;\n const endY = trail.fromY + (trail.toY - trail.fromY) * trail.progress;\n ctx.lineTo(endX, endY);\n } else {\n ctx.lineTo(trail.toX, trail.toY);\n }\n ctx.stroke();\n\n ctx.shadowColor = lightColor;\n ctx.shadowBlur = 2;\n ctx.stroke();\n ctx.shadowBlur = 0;\n }\n };\n\n const drawParticles = (ctx: CanvasRenderingContext2D) => {\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n const x = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;\n const y = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n ctx.arc(x, y, 3, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const drawExplosions = (ctx: CanvasRenderingContext2D) => {\n for (const explosion of explosionsRef.current) {\n if (explosion.opacity <= 0) continue;\n\n const alpha = Math.floor(explosion.opacity * 255).toString(16).padStart(2, '0');\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);\n ctx.stroke();\n }\n };\n\n const updateExplosions = () => {\n for (const explosion of explosionsRef.current) {\n explosion.radius += 0.5;\n explosion.opacity -= 0.08;\n }\n explosionsRef.current = explosionsRef.current.filter(e => e.opacity > 0);\n };\n\n const createExplosion = (x: number, y: number) => {\n explosionsRef.current.push({\n x,\n y,\n radius: 2,\n maxRadius: 8,\n opacity: 1,\n });\n };\n\n const createNodeGlow = (x: number, y: number) => {\n nodeGlowsRef.current.push({\n x,\n y,\n opacity: 1,\n });\n };\n\n const drawNodeGlows = (ctx: CanvasRenderingContext2D) => {\n for (const glow of nodeGlowsRef.current) {\n if (glow.opacity <= 0) continue;\n\n const alpha = Math.floor(glow.opacity * 0.4 * 255).toString(16).padStart(2, '0');\n ctx.fillStyle = lightColor + alpha;\n ctx.beginPath();\n ctx.arc(glow.x, glow.y, 2, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const updateNodeGlows = () => {\n for (const glow of nodeGlowsRef.current) {\n glow.opacity -= trailFadeSpeed * 3;\n }\n nodeGlowsRef.current = nodeGlowsRef.current.filter(g => g.opacity > 0);\n };\n\n const getRandomTravel = () => {\n return Math.floor(Math.random() * (maxTravel - minTravel + 1)) + minTravel;\n };\n\n const makeEdgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const spawnParticle = () => {\n if (topNodesRef.current.length === 0) return;\n\n const startNode = topNodesRef.current[Math.floor(Math.random() * topNodesRef.current.length)];\n const connectedNodes = getConnectedNodes(startNode);\n\n const downwardNodes = connectedNodes.filter(n => n.y > startNode.y);\n const targetNodes = downwardNodes.length > 0 ? downwardNodes : connectedNodes;\n\n const availableNodes = targetNodes.filter(n => {\n const key = makeEdgeKey(startNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) return;\n\n const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const edgeKeyStr = makeEdgeKey(startNode, targetNode);\n activeEdgesRef.current.add(edgeKeyStr);\n\n particlesRef.current.push({\n id: particleIdRef.current++,\n currentNode: startNode,\n targetNode,\n progress: 0,\n traveled: 0,\n maxTravel: getRandomTravel(),\n canSplit: true,\n dead: false,\n });\n\n trailsRef.current.push({\n fromX: startNode.x,\n fromY: startNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKeyStr,\n });\n };\n\n const updateParticles = () => {\n const newParticles: Particle[] = [];\n\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n particle.progress += lightSpeed * 0.02;\n\n const trailIndex = trailsRef.current.findIndex(\n t => t.fromX === particle.currentNode.x &&\n t.fromY === particle.currentNode.y &&\n t.toX === particle.targetNode.x &&\n t.toY === particle.targetNode.y &&\n t.progress < 1\n );\n if (trailIndex !== -1) {\n trailsRef.current[trailIndex].progress = Math.min(particle.progress, 1);\n }\n\n if (particle.progress >= 1) {\n createNodeGlow(particle.targetNode.x, particle.targetNode.y);\n particle.traveled++;\n\n if (particle.traveled >= particle.maxTravel) {\n const x = particle.targetNode.x;\n const y = particle.targetNode.y;\n createExplosion(x, y);\n particle.dead = true;\n continue;\n }\n\n const currentNode = particle.targetNode;\n const connectedNodes = getConnectedNodes(currentNode, particle.currentNode);\n\n const availableNodes = connectedNodes.filter(n => {\n const key = makeEdgeKey(currentNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) {\n createExplosion(currentNode.x, currentNode.y);\n particle.dead = true;\n continue;\n }\n\n const shouldSplit = particle.canSplit && Math.random() < splitChance && availableNodes.length >= 2;\n\n if (shouldSplit) {\n const shuffled = [...availableNodes].sort(() => Math.random() - 0.5);\n const target1 = shuffled[0];\n const target2 = shuffled[1];\n\n const edgeKey1 = makeEdgeKey(currentNode, target1);\n const edgeKey2 = makeEdgeKey(currentNode, target2);\n activeEdgesRef.current.add(edgeKey1);\n activeEdgesRef.current.add(edgeKey2);\n\n particle.currentNode = currentNode;\n particle.targetNode = target1;\n particle.progress = 0;\n particle.canSplit = false;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target1.x,\n toY: target1.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey1,\n });\n\n newParticles.push({\n id: particleIdRef.current++,\n currentNode: currentNode,\n targetNode: target2,\n progress: 0,\n traveled: particle.traveled,\n maxTravel: particle.maxTravel,\n canSplit: false,\n dead: false,\n });\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target2.x,\n toY: target2.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey2,\n });\n } else {\n const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const newEdgeKey = makeEdgeKey(currentNode, targetNode);\n activeEdgesRef.current.add(newEdgeKey);\n\n particle.currentNode = currentNode;\n particle.targetNode = targetNode;\n particle.progress = 0;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: newEdgeKey,\n });\n }\n }\n }\n\n particlesRef.current.push(...newParticles);\n particlesRef.current = particlesRef.current.filter(p => !p.dead);\n };\n\n const updateTrails = () => {\n for (const trail of trailsRef.current) {\n if (trail.progress >= 1) {\n trail.opacity -= trailFadeSpeed;\n }\n }\n const fadedTrails = trailsRef.current.filter(t => t.opacity <= 0);\n for (const trail of fadedTrails) {\n activeEdgesRef.current.delete(trail.edgeKey);\n }\n trailsRef.current = trailsRef.current.filter(t => t.opacity > 0);\n };\n\n const animate = (timestamp: number) => {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const { width, height } = dimensionsRef.current;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n drawGrid(ctx, width, height);\n\n if (animated) {\n if (timestamp - lastSpawnRef.current > spawnRate) {\n spawnParticle();\n lastSpawnRef.current = timestamp;\n }\n\n updateParticles();\n updateTrails();\n updateExplosions();\n updateNodeGlows();\n drawTrails(ctx);\n drawNodeGlows(ctx);\n drawParticles(ctx);\n drawExplosions(ctx);\n }\n\n ctx.restore();\n animationRef.current = requestAnimationFrame(animate);\n };\n\n const setup = () => {\n const { width, height } = container.getBoundingClientRect();\n dimensionsRef.current = { width, height };\n dpr = window.devicePixelRatio || 1;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n if (shape === 4) {\n buildSquareGraph(width, height);\n } else {\n buildHexGraph(width, height);\n }\n\n particlesRef.current = [];\n trailsRef.current = [];\n activeEdgesRef.current.clear();\n explosionsRef.current = [];\n nodeGlowsRef.current = [];\n };\n\n const resizeObserver = new ResizeObserver(() => {\n setup();\n });\n\n resizeObserver.observe(container);\n setup();\n animationRef.current = requestAnimationFrame(animate);\n\n return () => {\n resizeObserver.disconnect();\n cancelAnimationFrame(animationRef.current);\n };\n }, [shape, cellSize, lineColor, lineWidth, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, buildSquareGraph, buildHexGraph, getConnectedNodes]);\n\n return (\n <div ref={containerRef} className={`relative w-full h-full ${className}`}>\n <canvas ref={canvasRef} className=\"absolute inset-0\" />\n </div>\n );\n}\n\nexport default Grid;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/Grid.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useCallback } from 'react';\n\nexport interface GridProps {\n /** Grid shape: 4 for squares, 6 for hexagons */\n shape?: 4 | 6;\n /** Size of each cell in pixels */\n cellSize?: number;\n /** Color of the grid lines (use \"transparent\" to hide) */\n lineColor?: string;\n /** Width of the grid lines */\n lineWidth?: number;\n /** Additional CSS classes */\n className?: string;\n /** Enable animated light particles */\n animated?: boolean;\n /** Color of the light particles and trails */\n lightColor?: string;\n /** Speed of the light particles (1-5 recommended) */\n lightSpeed?: number;\n /** Minimum travel distance before particle dies */\n minTravel?: number;\n /** Maximum travel distance before particle dies */\n maxTravel?: number;\n /** Milliseconds between particle spawns */\n spawnRate?: number;\n /** Probability of particle splitting (0-1) */\n splitChance?: number;\n /** Speed at which trails fade (lower = slower fade) */\n trailFadeSpeed?: number;\n /** Enable spawning particles from the bottom as well as the top */\n bidirectional?: boolean;\n}\n\ninterface Node {\n x: number;\n y: number;\n key: string;\n}\n\ninterface Edge {\n from: Node;\n to: Node;\n key: string;\n}\n\ninterface Particle {\n id: number;\n currentNode: Node;\n targetNode: Node;\n progress: number;\n traveled: number;\n maxTravel: number;\n canSplit: boolean;\n dead: boolean;\n direction: 1 | -1;\n history: { x: number; y: number }[];\n}\n\ninterface Trail {\n fromX: number;\n fromY: number;\n toX: number;\n toY: number;\n progress: number;\n opacity: number;\n edgeKey: string;\n}\n\ninterface Explosion {\n x: number;\n y: number;\n radius: number;\n maxRadius: number;\n opacity: number;\n}\n\ninterface NodeGlow {\n x: number;\n y: number;\n opacity: number;\n}\n\nexport function Grid({\n shape = 4,\n cellSize = 40,\n lineColor = '#e5e7eb',\n lineWidth = 1,\n className = '',\n animated = false,\n lightColor = '#3b82f6',\n lightSpeed = 2,\n minTravel = 2,\n maxTravel = 6,\n spawnRate = 1000,\n splitChance = 0.3,\n trailFadeSpeed = 0.01,\n bidirectional = false,\n}: GridProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const nodesRef = useRef<Map<string, Node>>(new Map());\n const edgesRef = useRef<Edge[]>([]);\n const adjacencyRef = useRef<Map<string, Edge[]>>(new Map());\n const topNodesRef = useRef<Node[]>([]);\n const bottomNodesRef = useRef<Node[]>([]);\n const animationRef = useRef<number>(0);\n const particlesRef = useRef<Particle[]>([]);\n const trailsRef = useRef<Trail[]>([]);\n const lastSpawnRef = useRef<number>(0);\n const particleIdRef = useRef<number>(0);\n const dimensionsRef = useRef({ width: 0, height: 0 });\n const activeEdgesRef = useRef<Set<string>>(new Set());\n const explosionsRef = useRef<Explosion[]>([]);\n const nodeGlowsRef = useRef<NodeGlow[]>([]);\n const spawnFromTopRef = useRef<boolean>(true);\n\n const nodeKey = (x: number, y: number) =>\n `${Math.round(x * 100)},${Math.round(y * 100)}`;\n const edgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const getOrCreateNode = useCallback((x: number, y: number): Node => {\n const key = nodeKey(x, y);\n if (!nodesRef.current.has(key)) {\n nodesRef.current.set(key, { x, y, key });\n }\n return nodesRef.current.get(key)!;\n }, []);\n\n const addEdge = useCallback(\n (x1: number, y1: number, x2: number, y2: number) => {\n const from = getOrCreateNode(x1, y1);\n const to = getOrCreateNode(x2, y2);\n const key = edgeKey(from, to);\n\n const existingEdge = edgesRef.current.find((e) => e.key === key);\n if (existingEdge) return;\n\n const edge: Edge = { from, to, key };\n edgesRef.current.push(edge);\n\n if (!adjacencyRef.current.has(from.key)) {\n adjacencyRef.current.set(from.key, []);\n }\n if (!adjacencyRef.current.has(to.key)) {\n adjacencyRef.current.set(to.key, []);\n }\n adjacencyRef.current.get(from.key)!.push(edge);\n adjacencyRef.current.get(to.key)!.push(edge);\n },\n [getOrCreateNode]\n );\n\n const buildSquareGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const cols = Math.ceil(width / cellSize) + 1;\n const rows = Math.ceil(height / cellSize) + 1;\n\n for (let i = 0; i <= cols; i++) {\n for (let j = 0; j <= rows; j++) {\n const x = i * cellSize;\n const y = j * cellSize;\n if (i < cols) addEdge(x, y, x + cellSize, y);\n if (j < rows) addEdge(x, y, x, y + cellSize);\n }\n }\n\n const maxY = rows * cellSize;\n for (let i = 0; i <= cols; i++) {\n const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));\n if (topNode) topNodesRef.current.push(topNode);\n\n const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));\n if (bottomNode) bottomNodesRef.current.push(bottomNode);\n }\n },\n [cellSize, addEdge]\n );\n\n const buildHexGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const size = cellSize / 2;\n const hexWidth = Math.sqrt(3) * size;\n const vertSpacing = size * 1.5;\n\n const cols = Math.ceil(width / hexWidth) + 2;\n const rows = Math.ceil(height / vertSpacing) + 2;\n\n for (let row = -1; row < rows; row++) {\n for (let col = -1; col < cols; col++) {\n const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);\n const cy = row * vertSpacing;\n\n const vertices: { x: number; y: number }[] = [];\n for (let i = 0; i < 6; i++) {\n const angle = (Math.PI / 3) * i - Math.PI / 2;\n vertices.push({\n x: cx + size * Math.cos(angle),\n y: cy + size * Math.sin(angle),\n });\n }\n\n for (let i = 0; i < 6; i++) {\n const v1 = vertices[i];\n const v2 = vertices[(i + 1) % 6];\n addEdge(v1.x, v1.y, v2.x, v2.y);\n }\n }\n }\n\n const allNodes = Array.from(nodesRef.current.values());\n\n const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);\n\n topNodesRef.current = visibleNodes\n .filter((n) => n.y <= size * 2)\n .sort((a, b) => a.x - b.x);\n\n bottomNodesRef.current = visibleNodes\n .filter((n) => n.y >= height - size * 2)\n .sort((a, b) => a.x - b.x);\n },\n [cellSize, addEdge]\n );\n\n const getConnectedNodes = useCallback(\n (node: Node, excludeNode?: Node): Node[] => {\n const edges = adjacencyRef.current.get(node.key) || [];\n return edges\n .map((e) => (e.from.key === node.key ? e.to : e.from))\n .filter((n) => !excludeNode || n.key !== excludeNode.key);\n },\n []\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container) return;\n\n let dpr = 1;\n\n const drawGrid = (\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number\n ) => {\n ctx.clearRect(0, 0, width, height);\n ctx.strokeStyle = lineColor;\n ctx.lineWidth = lineWidth;\n ctx.beginPath();\n\n for (const edge of edgesRef.current) {\n ctx.moveTo(edge.from.x, edge.from.y);\n ctx.lineTo(edge.to.x, edge.to.y);\n }\n\n ctx.stroke();\n };\n\n const drawTrails = (ctx: CanvasRenderingContext2D) => {\n for (const trail of trailsRef.current) {\n if (trail.opacity <= 0) continue;\n\n const alpha = Math.floor(trail.opacity * 0.08 * 255)\n .toString(16)\n .padStart(2, '0');\n\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 1;\n ctx.lineCap = 'round';\n ctx.beginPath();\n ctx.moveTo(trail.fromX, trail.fromY);\n\n if (trail.progress < 1) {\n const endX = trail.fromX + (trail.toX - trail.fromX) * trail.progress;\n const endY = trail.fromY + (trail.toY - trail.fromY) * trail.progress;\n ctx.lineTo(endX, endY);\n } else {\n ctx.lineTo(trail.toX, trail.toY);\n }\n ctx.stroke();\n\n ctx.shadowColor = lightColor;\n ctx.shadowBlur = 2;\n ctx.stroke();\n ctx.shadowBlur = 0;\n }\n };\n\n const drawParticles = (ctx: CanvasRenderingContext2D) => {\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n const history = particle.history;\n if (history.length === 0) continue;\n\n const head = history[history.length - 1];\n\n if (history.length >= 2) {\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.strokeStyle = lightColor + '40';\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(history[0].x, history[0].y);\n\n for (let i = 1; i < history.length; i++) {\n ctx.lineTo(history[i].x, history[i].y);\n }\n ctx.stroke();\n }\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n ctx.arc(head.x, head.y, 3, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const drawExplosions = (ctx: CanvasRenderingContext2D) => {\n for (const explosion of explosionsRef.current) {\n if (explosion.opacity <= 0) continue;\n\n const alpha = Math.floor(explosion.opacity * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);\n ctx.stroke();\n }\n };\n\n const updateExplosions = () => {\n for (const explosion of explosionsRef.current) {\n explosion.radius += 0.5;\n explosion.opacity -= 0.08;\n }\n explosionsRef.current = explosionsRef.current.filter(\n (e) => e.opacity > 0\n );\n };\n\n const createExplosion = (x: number, y: number) => {\n explosionsRef.current.push({\n x,\n y,\n radius: 2,\n maxRadius: 8,\n opacity: 1,\n });\n };\n\n const createNodeGlow = (x: number, y: number) => {\n nodeGlowsRef.current.push({\n x,\n y,\n opacity: 1,\n });\n };\n\n const drawNodeGlows = (ctx: CanvasRenderingContext2D) => {\n for (const glow of nodeGlowsRef.current) {\n if (glow.opacity <= 0) continue;\n\n const alpha = Math.floor(glow.opacity * 0.4 * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.fillStyle = lightColor + alpha;\n ctx.beginPath();\n ctx.arc(glow.x, glow.y, 2, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const updateNodeGlows = () => {\n for (const glow of nodeGlowsRef.current) {\n glow.opacity -= trailFadeSpeed * 3;\n }\n nodeGlowsRef.current = nodeGlowsRef.current.filter((g) => g.opacity > 0);\n };\n\n const getRandomTravel = () => {\n return (\n Math.floor(Math.random() * (maxTravel - minTravel + 1)) + minTravel\n );\n };\n\n const makeEdgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const trySpawn = (fromTop: boolean): boolean => {\n const sourceNodes = fromTop\n ? topNodesRef.current\n : bottomNodesRef.current;\n const direction: 1 | -1 = fromTop ? 1 : -1;\n\n if (sourceNodes.length === 0) return false;\n\n const startNode =\n sourceNodes[Math.floor(Math.random() * sourceNodes.length)];\n const connectedNodes = getConnectedNodes(startNode);\n\n const preferredNodes = connectedNodes.filter((n) =>\n fromTop ? n.y > startNode.y : n.y < startNode.y\n );\n const targetNodes =\n preferredNodes.length > 0 ? preferredNodes : connectedNodes;\n\n const availableNodes = targetNodes.filter((n) => {\n const key = makeEdgeKey(startNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) return false;\n\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const edgeKeyStr = makeEdgeKey(startNode, targetNode);\n activeEdgesRef.current.add(edgeKeyStr);\n\n particlesRef.current.push({\n id: particleIdRef.current++,\n currentNode: startNode,\n targetNode,\n progress: 0,\n traveled: 0,\n maxTravel: getRandomTravel(),\n canSplit: true,\n dead: false,\n direction,\n history: [{ x: startNode.x, y: startNode.y }],\n });\n\n trailsRef.current.push({\n fromX: startNode.x,\n fromY: startNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKeyStr,\n });\n\n return true;\n };\n\n const spawnParticle = () => {\n if (bidirectional) {\n const fromTop = spawnFromTopRef.current;\n spawnFromTopRef.current = !spawnFromTopRef.current;\n\n if (!trySpawn(fromTop)) {\n trySpawn(!fromTop);\n }\n } else {\n trySpawn(true);\n }\n };\n\n const updateParticles = () => {\n const newParticles: Particle[] = [];\n const maxHistoryLength = 10;\n\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n particle.progress += lightSpeed * 0.02;\n\n const currentX =\n particle.currentNode.x +\n (particle.targetNode.x - particle.currentNode.x) * particle.progress;\n const currentY =\n particle.currentNode.y +\n (particle.targetNode.y - particle.currentNode.y) * particle.progress;\n particle.history.push({ x: currentX, y: currentY });\n if (particle.history.length > maxHistoryLength) {\n particle.history.shift();\n }\n\n const trailIndex = trailsRef.current.findIndex(\n (t) =>\n t.fromX === particle.currentNode.x &&\n t.fromY === particle.currentNode.y &&\n t.toX === particle.targetNode.x &&\n t.toY === particle.targetNode.y &&\n t.progress < 1\n );\n if (trailIndex !== -1) {\n trailsRef.current[trailIndex].progress = Math.min(\n particle.progress,\n 1\n );\n }\n\n if (particle.progress >= 1) {\n createNodeGlow(particle.targetNode.x, particle.targetNode.y);\n particle.traveled++;\n\n if (particle.traveled >= particle.maxTravel) {\n const x = particle.targetNode.x;\n const y = particle.targetNode.y;\n createExplosion(x, y);\n particle.dead = true;\n continue;\n }\n\n const currentNode = particle.targetNode;\n const connectedNodes = getConnectedNodes(\n currentNode,\n particle.currentNode\n );\n\n const availableNodes = connectedNodes.filter((n) => {\n const key = makeEdgeKey(currentNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) {\n createExplosion(currentNode.x, currentNode.y);\n particle.dead = true;\n continue;\n }\n\n const shouldSplit =\n particle.canSplit &&\n Math.random() < splitChance &&\n availableNodes.length >= 2;\n\n if (shouldSplit) {\n const shuffled = [...availableNodes].sort(\n () => Math.random() - 0.5\n );\n const target1 = shuffled[0];\n const target2 = shuffled[1];\n\n const edgeKey1 = makeEdgeKey(currentNode, target1);\n const edgeKey2 = makeEdgeKey(currentNode, target2);\n activeEdgesRef.current.add(edgeKey1);\n activeEdgesRef.current.add(edgeKey2);\n\n particle.currentNode = currentNode;\n particle.targetNode = target1;\n particle.progress = 0;\n particle.canSplit = false;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target1.x,\n toY: target1.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey1,\n });\n\n newParticles.push({\n id: particleIdRef.current++,\n currentNode: currentNode,\n targetNode: target2,\n progress: 0,\n traveled: particle.traveled,\n maxTravel: particle.maxTravel,\n canSplit: false,\n dead: false,\n direction: particle.direction,\n history: [{ x: currentNode.x, y: currentNode.y }],\n });\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target2.x,\n toY: target2.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey2,\n });\n } else {\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const newEdgeKey = makeEdgeKey(currentNode, targetNode);\n activeEdgesRef.current.add(newEdgeKey);\n\n particle.currentNode = currentNode;\n particle.targetNode = targetNode;\n particle.progress = 0;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: newEdgeKey,\n });\n }\n }\n }\n\n particlesRef.current.push(...newParticles);\n particlesRef.current = particlesRef.current.filter((p) => !p.dead);\n };\n\n const updateTrails = () => {\n for (const trail of trailsRef.current) {\n if (trail.progress >= 1) {\n trail.opacity -= trailFadeSpeed;\n }\n }\n const fadedTrails = trailsRef.current.filter((t) => t.opacity <= 0);\n for (const trail of fadedTrails) {\n activeEdgesRef.current.delete(trail.edgeKey);\n }\n trailsRef.current = trailsRef.current.filter((t) => t.opacity > 0);\n };\n\n const animate = (timestamp: number) => {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const { width, height } = dimensionsRef.current;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n drawGrid(ctx, width, height);\n\n if (animated) {\n if (timestamp - lastSpawnRef.current > spawnRate) {\n spawnParticle();\n lastSpawnRef.current = timestamp;\n }\n\n updateParticles();\n updateTrails();\n updateExplosions();\n updateNodeGlows();\n drawTrails(ctx);\n drawNodeGlows(ctx);\n drawParticles(ctx);\n drawExplosions(ctx);\n }\n\n ctx.restore();\n animationRef.current = requestAnimationFrame(animate);\n };\n\n const setup = () => {\n const { width, height } = container.getBoundingClientRect();\n dimensionsRef.current = { width, height };\n dpr = window.devicePixelRatio || 1;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n if (shape === 4) {\n buildSquareGraph(width, height);\n } else {\n buildHexGraph(width, height);\n }\n\n particlesRef.current = [];\n trailsRef.current = [];\n activeEdgesRef.current.clear();\n explosionsRef.current = [];\n nodeGlowsRef.current = [];\n };\n\n const resizeObserver = new ResizeObserver(() => {\n setup();\n });\n\n resizeObserver.observe(container);\n setup();\n animationRef.current = requestAnimationFrame(animate);\n\n return () => {\n resizeObserver.disconnect();\n cancelAnimationFrame(animationRef.current);\n };\n }, [\n shape,\n cellSize,\n lineColor,\n lineWidth,\n animated,\n lightColor,\n lightSpeed,\n minTravel,\n maxTravel,\n spawnRate,\n splitChance,\n trailFadeSpeed,\n bidirectional,\n buildSquareGraph,\n buildHexGraph,\n getConnectedNodes,\n ]);\n\n return (\n <div ref={containerRef} className={`relative w-full h-full ${className}`}>\n <canvas ref={canvasRef} className=\"absolute inset-0\" />\n </div>\n );\n}\n\nexport default Grid;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-grid-lights",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A React component for animated grid backgrounds with traveling light particles. Supports square and hexagonal grids with customizable animations.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|