react-grid-lights 1.0.0 → 1.0.2

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 CHANGED
@@ -27,7 +27,11 @@ 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;
32
+ /** Length of the light trail (number of history points) */
33
+ trailLength?: number;
30
34
  }
31
- declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, }: GridProps): react_jsx_runtime.JSX.Element;
35
+ declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, bidirectional, trailLength, }: GridProps): react_jsx_runtime.JSX.Element;
32
36
 
33
37
  export { Grid, type GridProps, Grid as default };
package/dist/index.d.ts CHANGED
@@ -27,7 +27,11 @@ 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;
32
+ /** Length of the light trail (number of history points) */
33
+ trailLength?: number;
30
34
  }
31
- declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, }: GridProps): react_jsx_runtime.JSX.Element;
35
+ declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, bidirectional, trailLength, }: GridProps): react_jsx_runtime.JSX.Element;
32
36
 
33
37
  export { Grid, type GridProps, Grid as default };
package/dist/index.js CHANGED
@@ -42,7 +42,9 @@ 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,
47
+ trailLength = 15
46
48
  }) {
47
49
  const canvasRef = (0, import_react.useRef)(null);
48
50
  const containerRef = (0, import_react.useRef)(null);
@@ -50,6 +52,7 @@ function Grid({
50
52
  const edgesRef = (0, import_react.useRef)([]);
51
53
  const adjacencyRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
52
54
  const topNodesRef = (0, import_react.useRef)([]);
55
+ const bottomNodesRef = (0, import_react.useRef)([]);
53
56
  const animationRef = (0, import_react.useRef)(0);
54
57
  const particlesRef = (0, import_react.useRef)([]);
55
58
  const trailsRef = (0, import_react.useRef)([]);
@@ -59,6 +62,7 @@ function Grid({
59
62
  const activeEdgesRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
60
63
  const explosionsRef = (0, import_react.useRef)([]);
61
64
  const nodeGlowsRef = (0, import_react.useRef)([]);
65
+ const spawnFromTopRef = (0, import_react.useRef)(true);
62
66
  const nodeKey = (x, y) => `${Math.round(x * 100)},${Math.round(y * 100)}`;
63
67
  const edgeKey = (n1, n2) => {
64
68
  const keys = [n1.key, n2.key].sort();
@@ -71,79 +75,98 @@ function Grid({
71
75
  }
72
76
  return nodesRef.current.get(key);
73
77
  }, []);
74
- const addEdge = (0, import_react.useCallback)((x1, y1, x2, y2) => {
75
- const from = getOrCreateNode(x1, y1);
76
- const to = getOrCreateNode(x2, y2);
77
- const key = edgeKey(from, to);
78
- const existingEdge = edgesRef.current.find((e) => e.key === key);
79
- if (existingEdge) return;
80
- const edge = { from, to, key };
81
- edgesRef.current.push(edge);
82
- if (!adjacencyRef.current.has(from.key)) {
83
- adjacencyRef.current.set(from.key, []);
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);
78
+ const addEdge = (0, import_react.useCallback)(
79
+ (x1, y1, x2, y2) => {
80
+ const from = getOrCreateNode(x1, y1);
81
+ const to = getOrCreateNode(x2, y2);
82
+ const key = edgeKey(from, to);
83
+ const existingEdge = edgesRef.current.find((e) => e.key === key);
84
+ if (existingEdge) return;
85
+ const edge = { from, to, key };
86
+ edgesRef.current.push(edge);
87
+ if (!adjacencyRef.current.has(from.key)) {
88
+ adjacencyRef.current.set(from.key, []);
104
89
  }
105
- }
106
- for (let i = 0; i <= cols; i++) {
107
- const node = nodesRef.current.get(nodeKey(i * cellSize, 0));
108
- if (node) topNodesRef.current.push(node);
109
- }
110
- }, [cellSize, addEdge]);
111
- const buildHexGraph = (0, import_react.useCallback)((width, height) => {
112
- nodesRef.current.clear();
113
- edgesRef.current = [];
114
- adjacencyRef.current.clear();
115
- topNodesRef.current = [];
116
- const size = cellSize / 2;
117
- const hexWidth = Math.sqrt(3) * size;
118
- const vertSpacing = size * 1.5;
119
- const cols = Math.ceil(width / hexWidth) + 2;
120
- const rows = Math.ceil(height / vertSpacing) + 2;
121
- for (let row = -1; row < rows; row++) {
122
- for (let col = -1; col < cols; col++) {
123
- const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);
124
- const cy = row * vertSpacing;
125
- const vertices = [];
126
- for (let i = 0; i < 6; i++) {
127
- const angle = Math.PI / 3 * i - Math.PI / 2;
128
- vertices.push({
129
- x: cx + size * Math.cos(angle),
130
- y: cy + size * Math.sin(angle)
131
- });
90
+ if (!adjacencyRef.current.has(to.key)) {
91
+ adjacencyRef.current.set(to.key, []);
92
+ }
93
+ adjacencyRef.current.get(from.key).push(edge);
94
+ adjacencyRef.current.get(to.key).push(edge);
95
+ },
96
+ [getOrCreateNode]
97
+ );
98
+ const buildSquareGraph = (0, import_react.useCallback)(
99
+ (width, height) => {
100
+ nodesRef.current.clear();
101
+ edgesRef.current = [];
102
+ adjacencyRef.current.clear();
103
+ topNodesRef.current = [];
104
+ bottomNodesRef.current = [];
105
+ const cols = Math.ceil(width / cellSize) + 1;
106
+ const rows = Math.ceil(height / cellSize) + 1;
107
+ for (let i = 0; i <= cols; i++) {
108
+ for (let j = 0; j <= rows; j++) {
109
+ const x = i * cellSize;
110
+ const y = j * cellSize;
111
+ if (i < cols) addEdge(x, y, x + cellSize, y);
112
+ if (j < rows) addEdge(x, y, x, y + cellSize);
132
113
  }
133
- for (let i = 0; i < 6; i++) {
134
- const v1 = vertices[i];
135
- const v2 = vertices[(i + 1) % 6];
136
- addEdge(v1.x, v1.y, v2.x, v2.y);
114
+ }
115
+ const maxY = rows * cellSize;
116
+ for (let i = 0; i <= cols; i++) {
117
+ const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));
118
+ if (topNode) topNodesRef.current.push(topNode);
119
+ const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));
120
+ if (bottomNode) bottomNodesRef.current.push(bottomNode);
121
+ }
122
+ },
123
+ [cellSize, addEdge]
124
+ );
125
+ const buildHexGraph = (0, import_react.useCallback)(
126
+ (width, height) => {
127
+ nodesRef.current.clear();
128
+ edgesRef.current = [];
129
+ adjacencyRef.current.clear();
130
+ topNodesRef.current = [];
131
+ bottomNodesRef.current = [];
132
+ const size = cellSize / 2;
133
+ const hexWidth = Math.sqrt(3) * size;
134
+ const vertSpacing = size * 1.5;
135
+ const cols = Math.ceil(width / hexWidth) + 2;
136
+ const rows = Math.ceil(height / vertSpacing) + 2;
137
+ for (let row = -1; row < rows; row++) {
138
+ for (let col = -1; col < cols; col++) {
139
+ const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);
140
+ const cy = row * vertSpacing;
141
+ const vertices = [];
142
+ for (let i = 0; i < 6; i++) {
143
+ const angle = Math.PI / 3 * i - Math.PI / 2;
144
+ vertices.push({
145
+ x: cx + size * Math.cos(angle),
146
+ y: cy + size * Math.sin(angle)
147
+ });
148
+ }
149
+ for (let i = 0; i < 6; i++) {
150
+ const v1 = vertices[i];
151
+ const v2 = vertices[(i + 1) % 6];
152
+ addEdge(v1.x, v1.y, v2.x, v2.y);
153
+ }
137
154
  }
138
155
  }
139
- }
140
- const sortedNodes = Array.from(nodesRef.current.values()).filter((n) => n.y >= 0 && n.y <= size).sort((a, b) => a.x - b.x);
141
- topNodesRef.current = sortedNodes;
142
- }, [cellSize, addEdge]);
143
- const getConnectedNodes = (0, import_react.useCallback)((node, excludeNode) => {
144
- const edges = adjacencyRef.current.get(node.key) || [];
145
- return edges.map((e) => e.from.key === node.key ? e.to : e.from).filter((n) => !excludeNode || n.key !== excludeNode.key);
146
- }, []);
156
+ const allNodes = Array.from(nodesRef.current.values());
157
+ const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);
158
+ topNodesRef.current = visibleNodes.filter((n) => n.y <= size * 2).sort((a, b) => a.x - b.x);
159
+ bottomNodesRef.current = visibleNodes.filter((n) => n.y >= height - size * 2).sort((a, b) => a.x - b.x);
160
+ },
161
+ [cellSize, addEdge]
162
+ );
163
+ const getConnectedNodes = (0, import_react.useCallback)(
164
+ (node, excludeNode) => {
165
+ const edges = adjacencyRef.current.get(node.key) || [];
166
+ return edges.map((e) => e.from.key === node.key ? e.to : e.from).filter((n) => !excludeNode || n.key !== excludeNode.key);
167
+ },
168
+ []
169
+ );
147
170
  (0, import_react.useEffect)(() => {
148
171
  const canvas = canvasRef.current;
149
172
  const container = containerRef.current;
@@ -186,11 +209,44 @@ function Grid({
186
209
  const drawParticles = (ctx) => {
187
210
  for (const particle of particlesRef.current) {
188
211
  if (particle.dead) continue;
189
- const x = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;
190
- const y = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;
212
+ const history = particle.history;
213
+ if (history.length === 0) continue;
214
+ const head = history[history.length - 1];
215
+ if (history.length >= 2) {
216
+ ctx.lineCap = "round";
217
+ ctx.lineJoin = "round";
218
+ const r = parseInt(lightColor.slice(1, 3), 16);
219
+ const g = parseInt(lightColor.slice(3, 5), 16);
220
+ const b = parseInt(lightColor.slice(5, 7), 16);
221
+ const lighterR = Math.floor(r + (255 - r) * 0.6);
222
+ const lighterG = Math.floor(g + (255 - g) * 0.6);
223
+ const lighterB = Math.floor(b + (255 - b) * 0.6);
224
+ ctx.strokeStyle = `rgb(${lighterR}, ${lighterG}, ${lighterB})`;
225
+ const maxWidth = 4;
226
+ const minWidth = 0.5;
227
+ for (let i = 1; i < history.length; i++) {
228
+ const progress = i / (history.length - 1);
229
+ ctx.lineWidth = minWidth + (maxWidth - minWidth) * progress;
230
+ ctx.beginPath();
231
+ ctx.moveTo(history[i - 1].x, history[i - 1].y);
232
+ ctx.lineTo(history[i].x, history[i].y);
233
+ ctx.stroke();
234
+ }
235
+ }
191
236
  ctx.fillStyle = lightColor;
192
237
  ctx.beginPath();
193
- ctx.arc(x, y, 3, 0, Math.PI * 2);
238
+ let angle = 0;
239
+ if (history.length >= 2) {
240
+ const prev = history[history.length - 2];
241
+ angle = Math.atan2(head.y - prev.y, head.x - prev.x);
242
+ }
243
+ ctx.save();
244
+ ctx.translate(head.x, head.y);
245
+ ctx.rotate(angle);
246
+ ctx.moveTo(-3, 0);
247
+ ctx.quadraticCurveTo(1, -2.5, 2, 0);
248
+ ctx.quadraticCurveTo(1, 2.5, -3, 0);
249
+ ctx.restore();
194
250
  ctx.fill();
195
251
  }
196
252
  };
@@ -210,7 +266,9 @@ function Grid({
210
266
  explosion.radius += 0.5;
211
267
  explosion.opacity -= 0.08;
212
268
  }
213
- explosionsRef.current = explosionsRef.current.filter((e) => e.opacity > 0);
269
+ explosionsRef.current = explosionsRef.current.filter(
270
+ (e) => e.opacity > 0
271
+ );
214
272
  };
215
273
  const createExplosion = (x, y) => {
216
274
  explosionsRef.current.push({
@@ -251,17 +309,21 @@ function Grid({
251
309
  const keys = [n1.key, n2.key].sort();
252
310
  return `${keys[0]}-${keys[1]}`;
253
311
  };
254
- const spawnParticle = () => {
255
- if (topNodesRef.current.length === 0) return;
256
- const startNode = topNodesRef.current[Math.floor(Math.random() * topNodesRef.current.length)];
312
+ const trySpawn = (fromTop) => {
313
+ const sourceNodes = fromTop ? topNodesRef.current : bottomNodesRef.current;
314
+ const direction = fromTop ? 1 : -1;
315
+ if (sourceNodes.length === 0) return false;
316
+ const startNode = sourceNodes[Math.floor(Math.random() * sourceNodes.length)];
257
317
  const connectedNodes = getConnectedNodes(startNode);
258
- const downwardNodes = connectedNodes.filter((n) => n.y > startNode.y);
259
- const targetNodes = downwardNodes.length > 0 ? downwardNodes : connectedNodes;
318
+ const preferredNodes = connectedNodes.filter(
319
+ (n) => fromTop ? n.y > startNode.y : n.y < startNode.y
320
+ );
321
+ const targetNodes = preferredNodes.length > 0 ? preferredNodes : connectedNodes;
260
322
  const availableNodes = targetNodes.filter((n) => {
261
323
  const key = makeEdgeKey(startNode, n);
262
324
  return !activeEdgesRef.current.has(key);
263
325
  });
264
- if (availableNodes.length === 0) return;
326
+ if (availableNodes.length === 0) return false;
265
327
  const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];
266
328
  const edgeKeyStr = makeEdgeKey(startNode, targetNode);
267
329
  activeEdgesRef.current.add(edgeKeyStr);
@@ -273,7 +335,9 @@ function Grid({
273
335
  traveled: 0,
274
336
  maxTravel: getRandomTravel(),
275
337
  canSplit: true,
276
- dead: false
338
+ dead: false,
339
+ direction,
340
+ history: [{ x: startNode.x, y: startNode.y }]
277
341
  });
278
342
  trailsRef.current.push({
279
343
  fromX: startNode.x,
@@ -284,17 +348,39 @@ function Grid({
284
348
  opacity: 1,
285
349
  edgeKey: edgeKeyStr
286
350
  });
351
+ return true;
352
+ };
353
+ const spawnParticle = () => {
354
+ if (bidirectional) {
355
+ const fromTop = spawnFromTopRef.current;
356
+ spawnFromTopRef.current = !spawnFromTopRef.current;
357
+ if (!trySpawn(fromTop)) {
358
+ trySpawn(!fromTop);
359
+ }
360
+ } else {
361
+ trySpawn(true);
362
+ }
287
363
  };
288
364
  const updateParticles = () => {
289
365
  const newParticles = [];
366
+ const maxHistoryLength = trailLength;
290
367
  for (const particle of particlesRef.current) {
291
368
  if (particle.dead) continue;
292
369
  particle.progress += lightSpeed * 0.02;
370
+ const currentX = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;
371
+ const currentY = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;
372
+ particle.history.push({ x: currentX, y: currentY });
373
+ if (particle.history.length > maxHistoryLength) {
374
+ particle.history.shift();
375
+ }
293
376
  const trailIndex = trailsRef.current.findIndex(
294
377
  (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
378
  );
296
379
  if (trailIndex !== -1) {
297
- trailsRef.current[trailIndex].progress = Math.min(particle.progress, 1);
380
+ trailsRef.current[trailIndex].progress = Math.min(
381
+ particle.progress,
382
+ 1
383
+ );
298
384
  }
299
385
  if (particle.progress >= 1) {
300
386
  createNodeGlow(particle.targetNode.x, particle.targetNode.y);
@@ -307,7 +393,10 @@ function Grid({
307
393
  continue;
308
394
  }
309
395
  const currentNode = particle.targetNode;
310
- const connectedNodes = getConnectedNodes(currentNode, particle.currentNode);
396
+ const connectedNodes = getConnectedNodes(
397
+ currentNode,
398
+ particle.currentNode
399
+ );
311
400
  const availableNodes = connectedNodes.filter((n) => {
312
401
  const key = makeEdgeKey(currentNode, n);
313
402
  return !activeEdgesRef.current.has(key);
@@ -319,7 +408,9 @@ function Grid({
319
408
  }
320
409
  const shouldSplit = particle.canSplit && Math.random() < splitChance && availableNodes.length >= 2;
321
410
  if (shouldSplit) {
322
- const shuffled = [...availableNodes].sort(() => Math.random() - 0.5);
411
+ const shuffled = [...availableNodes].sort(
412
+ () => Math.random() - 0.5
413
+ );
323
414
  const target1 = shuffled[0];
324
415
  const target2 = shuffled[1];
325
416
  const edgeKey1 = makeEdgeKey(currentNode, target1);
@@ -347,7 +438,9 @@ function Grid({
347
438
  traveled: particle.traveled,
348
439
  maxTravel: particle.maxTravel,
349
440
  canSplit: false,
350
- dead: false
441
+ dead: false,
442
+ direction: particle.direction,
443
+ history: [{ x: currentNode.x, y: currentNode.y }]
351
444
  });
352
445
  trailsRef.current.push({
353
446
  fromX: currentNode.x,
@@ -445,7 +538,25 @@ function Grid({
445
538
  resizeObserver.disconnect();
446
539
  cancelAnimationFrame(animationRef.current);
447
540
  };
448
- }, [shape, cellSize, lineColor, lineWidth, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, buildSquareGraph, buildHexGraph, getConnectedNodes]);
541
+ }, [
542
+ shape,
543
+ cellSize,
544
+ lineColor,
545
+ lineWidth,
546
+ animated,
547
+ lightColor,
548
+ lightSpeed,
549
+ minTravel,
550
+ maxTravel,
551
+ spawnRate,
552
+ splitChance,
553
+ trailFadeSpeed,
554
+ bidirectional,
555
+ trailLength,
556
+ buildSquareGraph,
557
+ buildHexGraph,
558
+ getConnectedNodes
559
+ ]);
449
560
  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
561
  }
451
562
  // 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":"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","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 /** Length of the light trail (number of history points) */\n trailLength?: 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 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 trailLength = 15,\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 const r = parseInt(lightColor.slice(1, 3), 16);\n const g = parseInt(lightColor.slice(3, 5), 16);\n const b = parseInt(lightColor.slice(5, 7), 16);\n const lighterR = Math.floor(r + (255 - r) * 0.6);\n const lighterG = Math.floor(g + (255 - g) * 0.6);\n const lighterB = Math.floor(b + (255 - b) * 0.6);\n ctx.strokeStyle = `rgb(${lighterR}, ${lighterG}, ${lighterB})`;\n\n const maxWidth = 4;\n const minWidth = 0.5;\n for (let i = 1; i < history.length; i++) {\n const progress = i / (history.length - 1);\n ctx.lineWidth = minWidth + (maxWidth - minWidth) * progress;\n ctx.beginPath();\n ctx.moveTo(history[i - 1].x, history[i - 1].y);\n ctx.lineTo(history[i].x, history[i].y);\n ctx.stroke();\n }\n }\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n let angle = 0;\n if (history.length >= 2) {\n const prev = history[history.length - 2];\n angle = Math.atan2(head.y - prev.y, head.x - prev.x);\n }\n ctx.save();\n ctx.translate(head.x, head.y);\n ctx.rotate(angle);\n ctx.moveTo(-3, 0);\n ctx.quadraticCurveTo(1, -2.5, 2, 0);\n ctx.quadraticCurveTo(1, 2.5, -3, 0);\n ctx.restore();\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 = trailLength;\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 trailLength,\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,9 @@ 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,
21
+ trailLength = 15
20
22
  }) {
21
23
  const canvasRef = useRef(null);
22
24
  const containerRef = useRef(null);
@@ -24,6 +26,7 @@ function Grid({
24
26
  const edgesRef = useRef([]);
25
27
  const adjacencyRef = useRef(/* @__PURE__ */ new Map());
26
28
  const topNodesRef = useRef([]);
29
+ const bottomNodesRef = useRef([]);
27
30
  const animationRef = useRef(0);
28
31
  const particlesRef = useRef([]);
29
32
  const trailsRef = useRef([]);
@@ -33,6 +36,7 @@ function Grid({
33
36
  const activeEdgesRef = useRef(/* @__PURE__ */ new Set());
34
37
  const explosionsRef = useRef([]);
35
38
  const nodeGlowsRef = useRef([]);
39
+ const spawnFromTopRef = useRef(true);
36
40
  const nodeKey = (x, y) => `${Math.round(x * 100)},${Math.round(y * 100)}`;
37
41
  const edgeKey = (n1, n2) => {
38
42
  const keys = [n1.key, n2.key].sort();
@@ -45,79 +49,98 @@ function Grid({
45
49
  }
46
50
  return nodesRef.current.get(key);
47
51
  }, []);
48
- const addEdge = useCallback((x1, y1, x2, y2) => {
49
- const from = getOrCreateNode(x1, y1);
50
- const to = getOrCreateNode(x2, y2);
51
- const key = edgeKey(from, to);
52
- const existingEdge = edgesRef.current.find((e) => e.key === key);
53
- if (existingEdge) return;
54
- const edge = { from, to, key };
55
- edgesRef.current.push(edge);
56
- if (!adjacencyRef.current.has(from.key)) {
57
- adjacencyRef.current.set(from.key, []);
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);
52
+ const addEdge = useCallback(
53
+ (x1, y1, x2, y2) => {
54
+ const from = getOrCreateNode(x1, y1);
55
+ const to = getOrCreateNode(x2, y2);
56
+ const key = edgeKey(from, to);
57
+ const existingEdge = edgesRef.current.find((e) => e.key === key);
58
+ if (existingEdge) return;
59
+ const edge = { from, to, key };
60
+ edgesRef.current.push(edge);
61
+ if (!adjacencyRef.current.has(from.key)) {
62
+ adjacencyRef.current.set(from.key, []);
78
63
  }
79
- }
80
- for (let i = 0; i <= cols; i++) {
81
- const node = nodesRef.current.get(nodeKey(i * cellSize, 0));
82
- if (node) topNodesRef.current.push(node);
83
- }
84
- }, [cellSize, addEdge]);
85
- const buildHexGraph = useCallback((width, height) => {
86
- nodesRef.current.clear();
87
- edgesRef.current = [];
88
- adjacencyRef.current.clear();
89
- topNodesRef.current = [];
90
- const size = cellSize / 2;
91
- const hexWidth = Math.sqrt(3) * size;
92
- const vertSpacing = size * 1.5;
93
- const cols = Math.ceil(width / hexWidth) + 2;
94
- const rows = Math.ceil(height / vertSpacing) + 2;
95
- for (let row = -1; row < rows; row++) {
96
- for (let col = -1; col < cols; col++) {
97
- const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);
98
- const cy = row * vertSpacing;
99
- const vertices = [];
100
- for (let i = 0; i < 6; i++) {
101
- const angle = Math.PI / 3 * i - Math.PI / 2;
102
- vertices.push({
103
- x: cx + size * Math.cos(angle),
104
- y: cy + size * Math.sin(angle)
105
- });
64
+ if (!adjacencyRef.current.has(to.key)) {
65
+ adjacencyRef.current.set(to.key, []);
66
+ }
67
+ adjacencyRef.current.get(from.key).push(edge);
68
+ adjacencyRef.current.get(to.key).push(edge);
69
+ },
70
+ [getOrCreateNode]
71
+ );
72
+ const buildSquareGraph = useCallback(
73
+ (width, height) => {
74
+ nodesRef.current.clear();
75
+ edgesRef.current = [];
76
+ adjacencyRef.current.clear();
77
+ topNodesRef.current = [];
78
+ bottomNodesRef.current = [];
79
+ const cols = Math.ceil(width / cellSize) + 1;
80
+ const rows = Math.ceil(height / cellSize) + 1;
81
+ for (let i = 0; i <= cols; i++) {
82
+ for (let j = 0; j <= rows; j++) {
83
+ const x = i * cellSize;
84
+ const y = j * cellSize;
85
+ if (i < cols) addEdge(x, y, x + cellSize, y);
86
+ if (j < rows) addEdge(x, y, x, y + cellSize);
106
87
  }
107
- for (let i = 0; i < 6; i++) {
108
- const v1 = vertices[i];
109
- const v2 = vertices[(i + 1) % 6];
110
- addEdge(v1.x, v1.y, v2.x, v2.y);
88
+ }
89
+ const maxY = rows * cellSize;
90
+ for (let i = 0; i <= cols; i++) {
91
+ const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));
92
+ if (topNode) topNodesRef.current.push(topNode);
93
+ const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));
94
+ if (bottomNode) bottomNodesRef.current.push(bottomNode);
95
+ }
96
+ },
97
+ [cellSize, addEdge]
98
+ );
99
+ const buildHexGraph = useCallback(
100
+ (width, height) => {
101
+ nodesRef.current.clear();
102
+ edgesRef.current = [];
103
+ adjacencyRef.current.clear();
104
+ topNodesRef.current = [];
105
+ bottomNodesRef.current = [];
106
+ const size = cellSize / 2;
107
+ const hexWidth = Math.sqrt(3) * size;
108
+ const vertSpacing = size * 1.5;
109
+ const cols = Math.ceil(width / hexWidth) + 2;
110
+ const rows = Math.ceil(height / vertSpacing) + 2;
111
+ for (let row = -1; row < rows; row++) {
112
+ for (let col = -1; col < cols; col++) {
113
+ const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);
114
+ const cy = row * vertSpacing;
115
+ const vertices = [];
116
+ for (let i = 0; i < 6; i++) {
117
+ const angle = Math.PI / 3 * i - Math.PI / 2;
118
+ vertices.push({
119
+ x: cx + size * Math.cos(angle),
120
+ y: cy + size * Math.sin(angle)
121
+ });
122
+ }
123
+ for (let i = 0; i < 6; i++) {
124
+ const v1 = vertices[i];
125
+ const v2 = vertices[(i + 1) % 6];
126
+ addEdge(v1.x, v1.y, v2.x, v2.y);
127
+ }
111
128
  }
112
129
  }
113
- }
114
- const sortedNodes = Array.from(nodesRef.current.values()).filter((n) => n.y >= 0 && n.y <= size).sort((a, b) => a.x - b.x);
115
- topNodesRef.current = sortedNodes;
116
- }, [cellSize, addEdge]);
117
- const getConnectedNodes = useCallback((node, excludeNode) => {
118
- const edges = adjacencyRef.current.get(node.key) || [];
119
- return edges.map((e) => e.from.key === node.key ? e.to : e.from).filter((n) => !excludeNode || n.key !== excludeNode.key);
120
- }, []);
130
+ const allNodes = Array.from(nodesRef.current.values());
131
+ const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);
132
+ topNodesRef.current = visibleNodes.filter((n) => n.y <= size * 2).sort((a, b) => a.x - b.x);
133
+ bottomNodesRef.current = visibleNodes.filter((n) => n.y >= height - size * 2).sort((a, b) => a.x - b.x);
134
+ },
135
+ [cellSize, addEdge]
136
+ );
137
+ const getConnectedNodes = useCallback(
138
+ (node, excludeNode) => {
139
+ const edges = adjacencyRef.current.get(node.key) || [];
140
+ return edges.map((e) => e.from.key === node.key ? e.to : e.from).filter((n) => !excludeNode || n.key !== excludeNode.key);
141
+ },
142
+ []
143
+ );
121
144
  useEffect(() => {
122
145
  const canvas = canvasRef.current;
123
146
  const container = containerRef.current;
@@ -160,11 +183,44 @@ function Grid({
160
183
  const drawParticles = (ctx) => {
161
184
  for (const particle of particlesRef.current) {
162
185
  if (particle.dead) continue;
163
- const x = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;
164
- const y = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;
186
+ const history = particle.history;
187
+ if (history.length === 0) continue;
188
+ const head = history[history.length - 1];
189
+ if (history.length >= 2) {
190
+ ctx.lineCap = "round";
191
+ ctx.lineJoin = "round";
192
+ const r = parseInt(lightColor.slice(1, 3), 16);
193
+ const g = parseInt(lightColor.slice(3, 5), 16);
194
+ const b = parseInt(lightColor.slice(5, 7), 16);
195
+ const lighterR = Math.floor(r + (255 - r) * 0.6);
196
+ const lighterG = Math.floor(g + (255 - g) * 0.6);
197
+ const lighterB = Math.floor(b + (255 - b) * 0.6);
198
+ ctx.strokeStyle = `rgb(${lighterR}, ${lighterG}, ${lighterB})`;
199
+ const maxWidth = 4;
200
+ const minWidth = 0.5;
201
+ for (let i = 1; i < history.length; i++) {
202
+ const progress = i / (history.length - 1);
203
+ ctx.lineWidth = minWidth + (maxWidth - minWidth) * progress;
204
+ ctx.beginPath();
205
+ ctx.moveTo(history[i - 1].x, history[i - 1].y);
206
+ ctx.lineTo(history[i].x, history[i].y);
207
+ ctx.stroke();
208
+ }
209
+ }
165
210
  ctx.fillStyle = lightColor;
166
211
  ctx.beginPath();
167
- ctx.arc(x, y, 3, 0, Math.PI * 2);
212
+ let angle = 0;
213
+ if (history.length >= 2) {
214
+ const prev = history[history.length - 2];
215
+ angle = Math.atan2(head.y - prev.y, head.x - prev.x);
216
+ }
217
+ ctx.save();
218
+ ctx.translate(head.x, head.y);
219
+ ctx.rotate(angle);
220
+ ctx.moveTo(-3, 0);
221
+ ctx.quadraticCurveTo(1, -2.5, 2, 0);
222
+ ctx.quadraticCurveTo(1, 2.5, -3, 0);
223
+ ctx.restore();
168
224
  ctx.fill();
169
225
  }
170
226
  };
@@ -184,7 +240,9 @@ function Grid({
184
240
  explosion.radius += 0.5;
185
241
  explosion.opacity -= 0.08;
186
242
  }
187
- explosionsRef.current = explosionsRef.current.filter((e) => e.opacity > 0);
243
+ explosionsRef.current = explosionsRef.current.filter(
244
+ (e) => e.opacity > 0
245
+ );
188
246
  };
189
247
  const createExplosion = (x, y) => {
190
248
  explosionsRef.current.push({
@@ -225,17 +283,21 @@ function Grid({
225
283
  const keys = [n1.key, n2.key].sort();
226
284
  return `${keys[0]}-${keys[1]}`;
227
285
  };
228
- const spawnParticle = () => {
229
- if (topNodesRef.current.length === 0) return;
230
- const startNode = topNodesRef.current[Math.floor(Math.random() * topNodesRef.current.length)];
286
+ const trySpawn = (fromTop) => {
287
+ const sourceNodes = fromTop ? topNodesRef.current : bottomNodesRef.current;
288
+ const direction = fromTop ? 1 : -1;
289
+ if (sourceNodes.length === 0) return false;
290
+ const startNode = sourceNodes[Math.floor(Math.random() * sourceNodes.length)];
231
291
  const connectedNodes = getConnectedNodes(startNode);
232
- const downwardNodes = connectedNodes.filter((n) => n.y > startNode.y);
233
- const targetNodes = downwardNodes.length > 0 ? downwardNodes : connectedNodes;
292
+ const preferredNodes = connectedNodes.filter(
293
+ (n) => fromTop ? n.y > startNode.y : n.y < startNode.y
294
+ );
295
+ const targetNodes = preferredNodes.length > 0 ? preferredNodes : connectedNodes;
234
296
  const availableNodes = targetNodes.filter((n) => {
235
297
  const key = makeEdgeKey(startNode, n);
236
298
  return !activeEdgesRef.current.has(key);
237
299
  });
238
- if (availableNodes.length === 0) return;
300
+ if (availableNodes.length === 0) return false;
239
301
  const targetNode = availableNodes[Math.floor(Math.random() * availableNodes.length)];
240
302
  const edgeKeyStr = makeEdgeKey(startNode, targetNode);
241
303
  activeEdgesRef.current.add(edgeKeyStr);
@@ -247,7 +309,9 @@ function Grid({
247
309
  traveled: 0,
248
310
  maxTravel: getRandomTravel(),
249
311
  canSplit: true,
250
- dead: false
312
+ dead: false,
313
+ direction,
314
+ history: [{ x: startNode.x, y: startNode.y }]
251
315
  });
252
316
  trailsRef.current.push({
253
317
  fromX: startNode.x,
@@ -258,17 +322,39 @@ function Grid({
258
322
  opacity: 1,
259
323
  edgeKey: edgeKeyStr
260
324
  });
325
+ return true;
326
+ };
327
+ const spawnParticle = () => {
328
+ if (bidirectional) {
329
+ const fromTop = spawnFromTopRef.current;
330
+ spawnFromTopRef.current = !spawnFromTopRef.current;
331
+ if (!trySpawn(fromTop)) {
332
+ trySpawn(!fromTop);
333
+ }
334
+ } else {
335
+ trySpawn(true);
336
+ }
261
337
  };
262
338
  const updateParticles = () => {
263
339
  const newParticles = [];
340
+ const maxHistoryLength = trailLength;
264
341
  for (const particle of particlesRef.current) {
265
342
  if (particle.dead) continue;
266
343
  particle.progress += lightSpeed * 0.02;
344
+ const currentX = particle.currentNode.x + (particle.targetNode.x - particle.currentNode.x) * particle.progress;
345
+ const currentY = particle.currentNode.y + (particle.targetNode.y - particle.currentNode.y) * particle.progress;
346
+ particle.history.push({ x: currentX, y: currentY });
347
+ if (particle.history.length > maxHistoryLength) {
348
+ particle.history.shift();
349
+ }
267
350
  const trailIndex = trailsRef.current.findIndex(
268
351
  (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
352
  );
270
353
  if (trailIndex !== -1) {
271
- trailsRef.current[trailIndex].progress = Math.min(particle.progress, 1);
354
+ trailsRef.current[trailIndex].progress = Math.min(
355
+ particle.progress,
356
+ 1
357
+ );
272
358
  }
273
359
  if (particle.progress >= 1) {
274
360
  createNodeGlow(particle.targetNode.x, particle.targetNode.y);
@@ -281,7 +367,10 @@ function Grid({
281
367
  continue;
282
368
  }
283
369
  const currentNode = particle.targetNode;
284
- const connectedNodes = getConnectedNodes(currentNode, particle.currentNode);
370
+ const connectedNodes = getConnectedNodes(
371
+ currentNode,
372
+ particle.currentNode
373
+ );
285
374
  const availableNodes = connectedNodes.filter((n) => {
286
375
  const key = makeEdgeKey(currentNode, n);
287
376
  return !activeEdgesRef.current.has(key);
@@ -293,7 +382,9 @@ function Grid({
293
382
  }
294
383
  const shouldSplit = particle.canSplit && Math.random() < splitChance && availableNodes.length >= 2;
295
384
  if (shouldSplit) {
296
- const shuffled = [...availableNodes].sort(() => Math.random() - 0.5);
385
+ const shuffled = [...availableNodes].sort(
386
+ () => Math.random() - 0.5
387
+ );
297
388
  const target1 = shuffled[0];
298
389
  const target2 = shuffled[1];
299
390
  const edgeKey1 = makeEdgeKey(currentNode, target1);
@@ -321,7 +412,9 @@ function Grid({
321
412
  traveled: particle.traveled,
322
413
  maxTravel: particle.maxTravel,
323
414
  canSplit: false,
324
- dead: false
415
+ dead: false,
416
+ direction: particle.direction,
417
+ history: [{ x: currentNode.x, y: currentNode.y }]
325
418
  });
326
419
  trailsRef.current.push({
327
420
  fromX: currentNode.x,
@@ -419,7 +512,25 @@ function Grid({
419
512
  resizeObserver.disconnect();
420
513
  cancelAnimationFrame(animationRef.current);
421
514
  };
422
- }, [shape, cellSize, lineColor, lineWidth, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, buildSquareGraph, buildHexGraph, getConnectedNodes]);
515
+ }, [
516
+ shape,
517
+ cellSize,
518
+ lineColor,
519
+ lineWidth,
520
+ animated,
521
+ lightColor,
522
+ lightSpeed,
523
+ minTravel,
524
+ maxTravel,
525
+ spawnRate,
526
+ splitChance,
527
+ trailFadeSpeed,
528
+ bidirectional,
529
+ trailLength,
530
+ buildSquareGraph,
531
+ buildHexGraph,
532
+ getConnectedNodes
533
+ ]);
423
534
  return /* @__PURE__ */ jsx("div", { ref: containerRef, className: `relative w-full h-full ${className}`, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "absolute inset-0" }) });
424
535
  }
425
536
  export {
@@ -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 /** Length of the light trail (number of history points) */\n trailLength?: 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 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 trailLength = 15,\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 const r = parseInt(lightColor.slice(1, 3), 16);\n const g = parseInt(lightColor.slice(3, 5), 16);\n const b = parseInt(lightColor.slice(5, 7), 16);\n const lighterR = Math.floor(r + (255 - r) * 0.6);\n const lighterG = Math.floor(g + (255 - g) * 0.6);\n const lighterB = Math.floor(b + (255 - b) * 0.6);\n ctx.strokeStyle = `rgb(${lighterR}, ${lighterG}, ${lighterB})`;\n\n const maxWidth = 4;\n const minWidth = 0.5;\n for (let i = 1; i < history.length; i++) {\n const progress = i / (history.length - 1);\n ctx.lineWidth = minWidth + (maxWidth - minWidth) * progress;\n ctx.beginPath();\n ctx.moveTo(history[i - 1].x, history[i - 1].y);\n ctx.lineTo(history[i].x, history[i].y);\n ctx.stroke();\n }\n }\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n let angle = 0;\n if (history.length >= 2) {\n const prev = history[history.length - 2];\n angle = Math.atan2(head.y - prev.y, head.x - prev.x);\n }\n ctx.save();\n ctx.translate(head.x, head.y);\n ctx.rotate(angle);\n ctx.moveTo(-3, 0);\n ctx.quadraticCurveTo(1, -2.5, 2, 0);\n ctx.quadraticCurveTo(1, 2.5, -3, 0);\n ctx.restore();\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 = trailLength;\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 trailLength,\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.0",
3
+ "version": "1.0.2",
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",