react-grid-lights 1.0.1 → 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
@@ -29,7 +29,9 @@ interface GridProps {
29
29
  trailFadeSpeed?: number;
30
30
  /** Enable spawning particles from the bottom as well as the top */
31
31
  bidirectional?: boolean;
32
+ /** Length of the light trail (number of history points) */
33
+ trailLength?: number;
32
34
  }
33
- declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, bidirectional, }: 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;
34
36
 
35
37
  export { Grid, type GridProps, Grid as default };
package/dist/index.d.ts CHANGED
@@ -29,7 +29,9 @@ interface GridProps {
29
29
  trailFadeSpeed?: number;
30
30
  /** Enable spawning particles from the bottom as well as the top */
31
31
  bidirectional?: boolean;
32
+ /** Length of the light trail (number of history points) */
33
+ trailLength?: number;
32
34
  }
33
- declare function Grid({ shape, cellSize, lineColor, lineWidth, className, animated, lightColor, lightSpeed, minTravel, maxTravel, spawnRate, splitChance, trailFadeSpeed, bidirectional, }: 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;
34
36
 
35
37
  export { Grid, type GridProps, Grid as default };
package/dist/index.js CHANGED
@@ -43,7 +43,8 @@ function Grid({
43
43
  spawnRate = 1e3,
44
44
  splitChance = 0.3,
45
45
  trailFadeSpeed = 0.01,
46
- bidirectional = false
46
+ bidirectional = false,
47
+ trailLength = 15
47
48
  }) {
48
49
  const canvasRef = (0, import_react.useRef)(null);
49
50
  const containerRef = (0, import_react.useRef)(null);
@@ -214,18 +215,38 @@ function Grid({
214
215
  if (history.length >= 2) {
215
216
  ctx.lineCap = "round";
216
217
  ctx.lineJoin = "round";
217
- ctx.strokeStyle = lightColor + "40";
218
- ctx.lineWidth = 2;
219
- ctx.beginPath();
220
- ctx.moveTo(history[0].x, history[0].y);
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;
221
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);
222
232
  ctx.lineTo(history[i].x, history[i].y);
233
+ ctx.stroke();
223
234
  }
224
- ctx.stroke();
225
235
  }
226
236
  ctx.fillStyle = lightColor;
227
237
  ctx.beginPath();
228
- ctx.arc(head.x, head.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();
229
250
  ctx.fill();
230
251
  }
231
252
  };
@@ -342,7 +363,7 @@ function Grid({
342
363
  };
343
364
  const updateParticles = () => {
344
365
  const newParticles = [];
345
- const maxHistoryLength = 10;
366
+ const maxHistoryLength = trailLength;
346
367
  for (const particle of particlesRef.current) {
347
368
  if (particle.dead) continue;
348
369
  particle.progress += lightSpeed * 0.02;
@@ -531,6 +552,7 @@ function Grid({
531
552
  splitChance,
532
553
  trailFadeSpeed,
533
554
  bidirectional,
555
+ trailLength,
534
556
  buildSquareGraph,
535
557
  buildHexGraph,
536
558
  getConnectedNodes
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 /** Enable spawning particles from the bottom as well as the top */\n bidirectional?: boolean;\n}\n\ninterface Node {\n x: number;\n y: number;\n key: string;\n}\n\ninterface Edge {\n from: Node;\n to: Node;\n key: string;\n}\n\ninterface Particle {\n id: number;\n currentNode: Node;\n targetNode: Node;\n progress: number;\n traveled: number;\n maxTravel: number;\n canSplit: boolean;\n dead: boolean;\n direction: 1 | -1;\n history: { x: number; y: number }[];\n}\n\ninterface Trail {\n fromX: number;\n fromY: number;\n toX: number;\n toY: number;\n progress: number;\n opacity: number;\n edgeKey: string;\n}\n\ninterface Explosion {\n x: number;\n y: number;\n radius: number;\n maxRadius: number;\n opacity: number;\n}\n\ninterface NodeGlow {\n x: number;\n y: number;\n opacity: number;\n}\n\nexport function Grid({\n shape = 4,\n cellSize = 40,\n lineColor = '#e5e7eb',\n lineWidth = 1,\n className = '',\n animated = false,\n lightColor = '#3b82f6',\n lightSpeed = 2,\n minTravel = 2,\n maxTravel = 6,\n spawnRate = 1000,\n splitChance = 0.3,\n trailFadeSpeed = 0.01,\n bidirectional = false,\n}: GridProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const nodesRef = useRef<Map<string, Node>>(new Map());\n const edgesRef = useRef<Edge[]>([]);\n const adjacencyRef = useRef<Map<string, Edge[]>>(new Map());\n const topNodesRef = useRef<Node[]>([]);\n const bottomNodesRef = useRef<Node[]>([]);\n const animationRef = useRef<number>(0);\n const particlesRef = useRef<Particle[]>([]);\n const trailsRef = useRef<Trail[]>([]);\n const lastSpawnRef = useRef<number>(0);\n const particleIdRef = useRef<number>(0);\n const dimensionsRef = useRef({ width: 0, height: 0 });\n const activeEdgesRef = useRef<Set<string>>(new Set());\n const explosionsRef = useRef<Explosion[]>([]);\n const nodeGlowsRef = useRef<NodeGlow[]>([]);\n const spawnFromTopRef = useRef<boolean>(true);\n\n const nodeKey = (x: number, y: number) =>\n `${Math.round(x * 100)},${Math.round(y * 100)}`;\n const edgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const getOrCreateNode = useCallback((x: number, y: number): Node => {\n const key = nodeKey(x, y);\n if (!nodesRef.current.has(key)) {\n nodesRef.current.set(key, { x, y, key });\n }\n return nodesRef.current.get(key)!;\n }, []);\n\n const addEdge = useCallback(\n (x1: number, y1: number, x2: number, y2: number) => {\n const from = getOrCreateNode(x1, y1);\n const to = getOrCreateNode(x2, y2);\n const key = edgeKey(from, to);\n\n const existingEdge = edgesRef.current.find((e) => e.key === key);\n if (existingEdge) return;\n\n const edge: Edge = { from, to, key };\n edgesRef.current.push(edge);\n\n if (!adjacencyRef.current.has(from.key)) {\n adjacencyRef.current.set(from.key, []);\n }\n if (!adjacencyRef.current.has(to.key)) {\n adjacencyRef.current.set(to.key, []);\n }\n adjacencyRef.current.get(from.key)!.push(edge);\n adjacencyRef.current.get(to.key)!.push(edge);\n },\n [getOrCreateNode]\n );\n\n const buildSquareGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const cols = Math.ceil(width / cellSize) + 1;\n const rows = Math.ceil(height / cellSize) + 1;\n\n for (let i = 0; i <= cols; i++) {\n for (let j = 0; j <= rows; j++) {\n const x = i * cellSize;\n const y = j * cellSize;\n if (i < cols) addEdge(x, y, x + cellSize, y);\n if (j < rows) addEdge(x, y, x, y + cellSize);\n }\n }\n\n const maxY = rows * cellSize;\n for (let i = 0; i <= cols; i++) {\n const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));\n if (topNode) topNodesRef.current.push(topNode);\n\n const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));\n if (bottomNode) bottomNodesRef.current.push(bottomNode);\n }\n },\n [cellSize, addEdge]\n );\n\n const buildHexGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const size = cellSize / 2;\n const hexWidth = Math.sqrt(3) * size;\n const vertSpacing = size * 1.5;\n\n const cols = Math.ceil(width / hexWidth) + 2;\n const rows = Math.ceil(height / vertSpacing) + 2;\n\n for (let row = -1; row < rows; row++) {\n for (let col = -1; col < cols; col++) {\n const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);\n const cy = row * vertSpacing;\n\n const vertices: { x: number; y: number }[] = [];\n for (let i = 0; i < 6; i++) {\n const angle = (Math.PI / 3) * i - Math.PI / 2;\n vertices.push({\n x: cx + size * Math.cos(angle),\n y: cy + size * Math.sin(angle),\n });\n }\n\n for (let i = 0; i < 6; i++) {\n const v1 = vertices[i];\n const v2 = vertices[(i + 1) % 6];\n addEdge(v1.x, v1.y, v2.x, v2.y);\n }\n }\n }\n\n const allNodes = Array.from(nodesRef.current.values());\n\n const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);\n\n topNodesRef.current = visibleNodes\n .filter((n) => n.y <= size * 2)\n .sort((a, b) => a.x - b.x);\n\n bottomNodesRef.current = visibleNodes\n .filter((n) => n.y >= height - size * 2)\n .sort((a, b) => a.x - b.x);\n },\n [cellSize, addEdge]\n );\n\n const getConnectedNodes = useCallback(\n (node: Node, excludeNode?: Node): Node[] => {\n const edges = adjacencyRef.current.get(node.key) || [];\n return edges\n .map((e) => (e.from.key === node.key ? e.to : e.from))\n .filter((n) => !excludeNode || n.key !== excludeNode.key);\n },\n []\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container) return;\n\n let dpr = 1;\n\n const drawGrid = (\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number\n ) => {\n ctx.clearRect(0, 0, width, height);\n ctx.strokeStyle = lineColor;\n ctx.lineWidth = lineWidth;\n ctx.beginPath();\n\n for (const edge of edgesRef.current) {\n ctx.moveTo(edge.from.x, edge.from.y);\n ctx.lineTo(edge.to.x, edge.to.y);\n }\n\n ctx.stroke();\n };\n\n const drawTrails = (ctx: CanvasRenderingContext2D) => {\n for (const trail of trailsRef.current) {\n if (trail.opacity <= 0) continue;\n\n const alpha = Math.floor(trail.opacity * 0.08 * 255)\n .toString(16)\n .padStart(2, '0');\n\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 1;\n ctx.lineCap = 'round';\n ctx.beginPath();\n ctx.moveTo(trail.fromX, trail.fromY);\n\n if (trail.progress < 1) {\n const endX = trail.fromX + (trail.toX - trail.fromX) * trail.progress;\n const endY = trail.fromY + (trail.toY - trail.fromY) * trail.progress;\n ctx.lineTo(endX, endY);\n } else {\n ctx.lineTo(trail.toX, trail.toY);\n }\n ctx.stroke();\n\n ctx.shadowColor = lightColor;\n ctx.shadowBlur = 2;\n ctx.stroke();\n ctx.shadowBlur = 0;\n }\n };\n\n const drawParticles = (ctx: CanvasRenderingContext2D) => {\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n const history = particle.history;\n if (history.length === 0) continue;\n\n const head = history[history.length - 1];\n\n if (history.length >= 2) {\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.strokeStyle = lightColor + '40';\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(history[0].x, history[0].y);\n\n for (let i = 1; i < history.length; i++) {\n ctx.lineTo(history[i].x, history[i].y);\n }\n ctx.stroke();\n }\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n ctx.arc(head.x, head.y, 3, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const drawExplosions = (ctx: CanvasRenderingContext2D) => {\n for (const explosion of explosionsRef.current) {\n if (explosion.opacity <= 0) continue;\n\n const alpha = Math.floor(explosion.opacity * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);\n ctx.stroke();\n }\n };\n\n const updateExplosions = () => {\n for (const explosion of explosionsRef.current) {\n explosion.radius += 0.5;\n explosion.opacity -= 0.08;\n }\n explosionsRef.current = explosionsRef.current.filter(\n (e) => e.opacity > 0\n );\n };\n\n const createExplosion = (x: number, y: number) => {\n explosionsRef.current.push({\n x,\n y,\n radius: 2,\n maxRadius: 8,\n opacity: 1,\n });\n };\n\n const createNodeGlow = (x: number, y: number) => {\n nodeGlowsRef.current.push({\n x,\n y,\n opacity: 1,\n });\n };\n\n const drawNodeGlows = (ctx: CanvasRenderingContext2D) => {\n for (const glow of nodeGlowsRef.current) {\n if (glow.opacity <= 0) continue;\n\n const alpha = Math.floor(glow.opacity * 0.4 * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.fillStyle = lightColor + alpha;\n ctx.beginPath();\n ctx.arc(glow.x, glow.y, 2, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const updateNodeGlows = () => {\n for (const glow of nodeGlowsRef.current) {\n glow.opacity -= trailFadeSpeed * 3;\n }\n nodeGlowsRef.current = nodeGlowsRef.current.filter((g) => g.opacity > 0);\n };\n\n const getRandomTravel = () => {\n return (\n Math.floor(Math.random() * (maxTravel - minTravel + 1)) + minTravel\n );\n };\n\n const makeEdgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const trySpawn = (fromTop: boolean): boolean => {\n const sourceNodes = fromTop\n ? topNodesRef.current\n : bottomNodesRef.current;\n const direction: 1 | -1 = fromTop ? 1 : -1;\n\n if (sourceNodes.length === 0) return false;\n\n const startNode =\n sourceNodes[Math.floor(Math.random() * sourceNodes.length)];\n const connectedNodes = getConnectedNodes(startNode);\n\n const preferredNodes = connectedNodes.filter((n) =>\n fromTop ? n.y > startNode.y : n.y < startNode.y\n );\n const targetNodes =\n preferredNodes.length > 0 ? preferredNodes : connectedNodes;\n\n const availableNodes = targetNodes.filter((n) => {\n const key = makeEdgeKey(startNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) return false;\n\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const edgeKeyStr = makeEdgeKey(startNode, targetNode);\n activeEdgesRef.current.add(edgeKeyStr);\n\n particlesRef.current.push({\n id: particleIdRef.current++,\n currentNode: startNode,\n targetNode,\n progress: 0,\n traveled: 0,\n maxTravel: getRandomTravel(),\n canSplit: true,\n dead: false,\n direction,\n history: [{ x: startNode.x, y: startNode.y }],\n });\n\n trailsRef.current.push({\n fromX: startNode.x,\n fromY: startNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKeyStr,\n });\n\n return true;\n };\n\n const spawnParticle = () => {\n if (bidirectional) {\n const fromTop = spawnFromTopRef.current;\n spawnFromTopRef.current = !spawnFromTopRef.current;\n\n if (!trySpawn(fromTop)) {\n trySpawn(!fromTop);\n }\n } else {\n trySpawn(true);\n }\n };\n\n const updateParticles = () => {\n const newParticles: Particle[] = [];\n const maxHistoryLength = 10;\n\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n particle.progress += lightSpeed * 0.02;\n\n const currentX =\n particle.currentNode.x +\n (particle.targetNode.x - particle.currentNode.x) * particle.progress;\n const currentY =\n particle.currentNode.y +\n (particle.targetNode.y - particle.currentNode.y) * particle.progress;\n particle.history.push({ x: currentX, y: currentY });\n if (particle.history.length > maxHistoryLength) {\n particle.history.shift();\n }\n\n const trailIndex = trailsRef.current.findIndex(\n (t) =>\n t.fromX === particle.currentNode.x &&\n t.fromY === particle.currentNode.y &&\n t.toX === particle.targetNode.x &&\n t.toY === particle.targetNode.y &&\n t.progress < 1\n );\n if (trailIndex !== -1) {\n trailsRef.current[trailIndex].progress = Math.min(\n particle.progress,\n 1\n );\n }\n\n if (particle.progress >= 1) {\n createNodeGlow(particle.targetNode.x, particle.targetNode.y);\n particle.traveled++;\n\n if (particle.traveled >= particle.maxTravel) {\n const x = particle.targetNode.x;\n const y = particle.targetNode.y;\n createExplosion(x, y);\n particle.dead = true;\n continue;\n }\n\n const currentNode = particle.targetNode;\n const connectedNodes = getConnectedNodes(\n currentNode,\n particle.currentNode\n );\n\n const availableNodes = connectedNodes.filter((n) => {\n const key = makeEdgeKey(currentNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) {\n createExplosion(currentNode.x, currentNode.y);\n particle.dead = true;\n continue;\n }\n\n const shouldSplit =\n particle.canSplit &&\n Math.random() < splitChance &&\n availableNodes.length >= 2;\n\n if (shouldSplit) {\n const shuffled = [...availableNodes].sort(\n () => Math.random() - 0.5\n );\n const target1 = shuffled[0];\n const target2 = shuffled[1];\n\n const edgeKey1 = makeEdgeKey(currentNode, target1);\n const edgeKey2 = makeEdgeKey(currentNode, target2);\n activeEdgesRef.current.add(edgeKey1);\n activeEdgesRef.current.add(edgeKey2);\n\n particle.currentNode = currentNode;\n particle.targetNode = target1;\n particle.progress = 0;\n particle.canSplit = false;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target1.x,\n toY: target1.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey1,\n });\n\n newParticles.push({\n id: particleIdRef.current++,\n currentNode: currentNode,\n targetNode: target2,\n progress: 0,\n traveled: particle.traveled,\n maxTravel: particle.maxTravel,\n canSplit: false,\n dead: false,\n direction: particle.direction,\n history: [{ x: currentNode.x, y: currentNode.y }],\n });\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target2.x,\n toY: target2.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey2,\n });\n } else {\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const newEdgeKey = makeEdgeKey(currentNode, targetNode);\n activeEdgesRef.current.add(newEdgeKey);\n\n particle.currentNode = currentNode;\n particle.targetNode = targetNode;\n particle.progress = 0;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: newEdgeKey,\n });\n }\n }\n }\n\n particlesRef.current.push(...newParticles);\n particlesRef.current = particlesRef.current.filter((p) => !p.dead);\n };\n\n const updateTrails = () => {\n for (const trail of trailsRef.current) {\n if (trail.progress >= 1) {\n trail.opacity -= trailFadeSpeed;\n }\n }\n const fadedTrails = trailsRef.current.filter((t) => t.opacity <= 0);\n for (const trail of fadedTrails) {\n activeEdgesRef.current.delete(trail.edgeKey);\n }\n trailsRef.current = trailsRef.current.filter((t) => t.opacity > 0);\n };\n\n const animate = (timestamp: number) => {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const { width, height } = dimensionsRef.current;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n drawGrid(ctx, width, height);\n\n if (animated) {\n if (timestamp - lastSpawnRef.current > spawnRate) {\n spawnParticle();\n lastSpawnRef.current = timestamp;\n }\n\n updateParticles();\n updateTrails();\n updateExplosions();\n updateNodeGlows();\n drawTrails(ctx);\n drawNodeGlows(ctx);\n drawParticles(ctx);\n drawExplosions(ctx);\n }\n\n ctx.restore();\n animationRef.current = requestAnimationFrame(animate);\n };\n\n const setup = () => {\n const { width, height } = container.getBoundingClientRect();\n dimensionsRef.current = { width, height };\n dpr = window.devicePixelRatio || 1;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n if (shape === 4) {\n buildSquareGraph(width, height);\n } else {\n buildHexGraph(width, height);\n }\n\n particlesRef.current = [];\n trailsRef.current = [];\n activeEdgesRef.current.clear();\n explosionsRef.current = [];\n nodeGlowsRef.current = [];\n };\n\n const resizeObserver = new ResizeObserver(() => {\n setup();\n });\n\n resizeObserver.observe(container);\n setup();\n animationRef.current = requestAnimationFrame(animate);\n\n return () => {\n resizeObserver.disconnect();\n cancelAnimationFrame(animationRef.current);\n };\n }, [\n shape,\n cellSize,\n lineColor,\n lineWidth,\n animated,\n lightColor,\n lightSpeed,\n minTravel,\n maxTravel,\n spawnRate,\n splitChance,\n trailFadeSpeed,\n bidirectional,\n buildSquareGraph,\n buildHexGraph,\n getConnectedNodes,\n ]);\n\n return (\n <div ref={containerRef} className={`relative w-full h-full ${className}`}>\n <canvas ref={canvasRef} className=\"absolute inset-0\" />\n </div>\n );\n}\n\nexport default Grid;\n"],"mappings":"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","names":[]}
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
@@ -17,7 +17,8 @@ function Grid({
17
17
  spawnRate = 1e3,
18
18
  splitChance = 0.3,
19
19
  trailFadeSpeed = 0.01,
20
- bidirectional = false
20
+ bidirectional = false,
21
+ trailLength = 15
21
22
  }) {
22
23
  const canvasRef = useRef(null);
23
24
  const containerRef = useRef(null);
@@ -188,18 +189,38 @@ function Grid({
188
189
  if (history.length >= 2) {
189
190
  ctx.lineCap = "round";
190
191
  ctx.lineJoin = "round";
191
- ctx.strokeStyle = lightColor + "40";
192
- ctx.lineWidth = 2;
193
- ctx.beginPath();
194
- ctx.moveTo(history[0].x, history[0].y);
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;
195
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);
196
206
  ctx.lineTo(history[i].x, history[i].y);
207
+ ctx.stroke();
197
208
  }
198
- ctx.stroke();
199
209
  }
200
210
  ctx.fillStyle = lightColor;
201
211
  ctx.beginPath();
202
- ctx.arc(head.x, head.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();
203
224
  ctx.fill();
204
225
  }
205
226
  };
@@ -316,7 +337,7 @@ function Grid({
316
337
  };
317
338
  const updateParticles = () => {
318
339
  const newParticles = [];
319
- const maxHistoryLength = 10;
340
+ const maxHistoryLength = trailLength;
320
341
  for (const particle of particlesRef.current) {
321
342
  if (particle.dead) continue;
322
343
  particle.progress += lightSpeed * 0.02;
@@ -505,6 +526,7 @@ function Grid({
505
526
  splitChance,
506
527
  trailFadeSpeed,
507
528
  bidirectional,
529
+ trailLength,
508
530
  buildSquareGraph,
509
531
  buildHexGraph,
510
532
  getConnectedNodes
@@ -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 /** Enable spawning particles from the bottom as well as the top */\n bidirectional?: boolean;\n}\n\ninterface Node {\n x: number;\n y: number;\n key: string;\n}\n\ninterface Edge {\n from: Node;\n to: Node;\n key: string;\n}\n\ninterface Particle {\n id: number;\n currentNode: Node;\n targetNode: Node;\n progress: number;\n traveled: number;\n maxTravel: number;\n canSplit: boolean;\n dead: boolean;\n direction: 1 | -1;\n history: { x: number; y: number }[];\n}\n\ninterface Trail {\n fromX: number;\n fromY: number;\n toX: number;\n toY: number;\n progress: number;\n opacity: number;\n edgeKey: string;\n}\n\ninterface Explosion {\n x: number;\n y: number;\n radius: number;\n maxRadius: number;\n opacity: number;\n}\n\ninterface NodeGlow {\n x: number;\n y: number;\n opacity: number;\n}\n\nexport function Grid({\n shape = 4,\n cellSize = 40,\n lineColor = '#e5e7eb',\n lineWidth = 1,\n className = '',\n animated = false,\n lightColor = '#3b82f6',\n lightSpeed = 2,\n minTravel = 2,\n maxTravel = 6,\n spawnRate = 1000,\n splitChance = 0.3,\n trailFadeSpeed = 0.01,\n bidirectional = false,\n}: GridProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const nodesRef = useRef<Map<string, Node>>(new Map());\n const edgesRef = useRef<Edge[]>([]);\n const adjacencyRef = useRef<Map<string, Edge[]>>(new Map());\n const topNodesRef = useRef<Node[]>([]);\n const bottomNodesRef = useRef<Node[]>([]);\n const animationRef = useRef<number>(0);\n const particlesRef = useRef<Particle[]>([]);\n const trailsRef = useRef<Trail[]>([]);\n const lastSpawnRef = useRef<number>(0);\n const particleIdRef = useRef<number>(0);\n const dimensionsRef = useRef({ width: 0, height: 0 });\n const activeEdgesRef = useRef<Set<string>>(new Set());\n const explosionsRef = useRef<Explosion[]>([]);\n const nodeGlowsRef = useRef<NodeGlow[]>([]);\n const spawnFromTopRef = useRef<boolean>(true);\n\n const nodeKey = (x: number, y: number) =>\n `${Math.round(x * 100)},${Math.round(y * 100)}`;\n const edgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const getOrCreateNode = useCallback((x: number, y: number): Node => {\n const key = nodeKey(x, y);\n if (!nodesRef.current.has(key)) {\n nodesRef.current.set(key, { x, y, key });\n }\n return nodesRef.current.get(key)!;\n }, []);\n\n const addEdge = useCallback(\n (x1: number, y1: number, x2: number, y2: number) => {\n const from = getOrCreateNode(x1, y1);\n const to = getOrCreateNode(x2, y2);\n const key = edgeKey(from, to);\n\n const existingEdge = edgesRef.current.find((e) => e.key === key);\n if (existingEdge) return;\n\n const edge: Edge = { from, to, key };\n edgesRef.current.push(edge);\n\n if (!adjacencyRef.current.has(from.key)) {\n adjacencyRef.current.set(from.key, []);\n }\n if (!adjacencyRef.current.has(to.key)) {\n adjacencyRef.current.set(to.key, []);\n }\n adjacencyRef.current.get(from.key)!.push(edge);\n adjacencyRef.current.get(to.key)!.push(edge);\n },\n [getOrCreateNode]\n );\n\n const buildSquareGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const cols = Math.ceil(width / cellSize) + 1;\n const rows = Math.ceil(height / cellSize) + 1;\n\n for (let i = 0; i <= cols; i++) {\n for (let j = 0; j <= rows; j++) {\n const x = i * cellSize;\n const y = j * cellSize;\n if (i < cols) addEdge(x, y, x + cellSize, y);\n if (j < rows) addEdge(x, y, x, y + cellSize);\n }\n }\n\n const maxY = rows * cellSize;\n for (let i = 0; i <= cols; i++) {\n const topNode = nodesRef.current.get(nodeKey(i * cellSize, 0));\n if (topNode) topNodesRef.current.push(topNode);\n\n const bottomNode = nodesRef.current.get(nodeKey(i * cellSize, maxY));\n if (bottomNode) bottomNodesRef.current.push(bottomNode);\n }\n },\n [cellSize, addEdge]\n );\n\n const buildHexGraph = useCallback(\n (width: number, height: number) => {\n nodesRef.current.clear();\n edgesRef.current = [];\n adjacencyRef.current.clear();\n topNodesRef.current = [];\n bottomNodesRef.current = [];\n\n const size = cellSize / 2;\n const hexWidth = Math.sqrt(3) * size;\n const vertSpacing = size * 1.5;\n\n const cols = Math.ceil(width / hexWidth) + 2;\n const rows = Math.ceil(height / vertSpacing) + 2;\n\n for (let row = -1; row < rows; row++) {\n for (let col = -1; col < cols; col++) {\n const cx = col * hexWidth + (row % 2 === 0 ? 0 : hexWidth / 2);\n const cy = row * vertSpacing;\n\n const vertices: { x: number; y: number }[] = [];\n for (let i = 0; i < 6; i++) {\n const angle = (Math.PI / 3) * i - Math.PI / 2;\n vertices.push({\n x: cx + size * Math.cos(angle),\n y: cy + size * Math.sin(angle),\n });\n }\n\n for (let i = 0; i < 6; i++) {\n const v1 = vertices[i];\n const v2 = vertices[(i + 1) % 6];\n addEdge(v1.x, v1.y, v2.x, v2.y);\n }\n }\n }\n\n const allNodes = Array.from(nodesRef.current.values());\n\n const visibleNodes = allNodes.filter((n) => n.y >= 0 && n.y <= height);\n\n topNodesRef.current = visibleNodes\n .filter((n) => n.y <= size * 2)\n .sort((a, b) => a.x - b.x);\n\n bottomNodesRef.current = visibleNodes\n .filter((n) => n.y >= height - size * 2)\n .sort((a, b) => a.x - b.x);\n },\n [cellSize, addEdge]\n );\n\n const getConnectedNodes = useCallback(\n (node: Node, excludeNode?: Node): Node[] => {\n const edges = adjacencyRef.current.get(node.key) || [];\n return edges\n .map((e) => (e.from.key === node.key ? e.to : e.from))\n .filter((n) => !excludeNode || n.key !== excludeNode.key);\n },\n []\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container) return;\n\n let dpr = 1;\n\n const drawGrid = (\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number\n ) => {\n ctx.clearRect(0, 0, width, height);\n ctx.strokeStyle = lineColor;\n ctx.lineWidth = lineWidth;\n ctx.beginPath();\n\n for (const edge of edgesRef.current) {\n ctx.moveTo(edge.from.x, edge.from.y);\n ctx.lineTo(edge.to.x, edge.to.y);\n }\n\n ctx.stroke();\n };\n\n const drawTrails = (ctx: CanvasRenderingContext2D) => {\n for (const trail of trailsRef.current) {\n if (trail.opacity <= 0) continue;\n\n const alpha = Math.floor(trail.opacity * 0.08 * 255)\n .toString(16)\n .padStart(2, '0');\n\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 1;\n ctx.lineCap = 'round';\n ctx.beginPath();\n ctx.moveTo(trail.fromX, trail.fromY);\n\n if (trail.progress < 1) {\n const endX = trail.fromX + (trail.toX - trail.fromX) * trail.progress;\n const endY = trail.fromY + (trail.toY - trail.fromY) * trail.progress;\n ctx.lineTo(endX, endY);\n } else {\n ctx.lineTo(trail.toX, trail.toY);\n }\n ctx.stroke();\n\n ctx.shadowColor = lightColor;\n ctx.shadowBlur = 2;\n ctx.stroke();\n ctx.shadowBlur = 0;\n }\n };\n\n const drawParticles = (ctx: CanvasRenderingContext2D) => {\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n const history = particle.history;\n if (history.length === 0) continue;\n\n const head = history[history.length - 1];\n\n if (history.length >= 2) {\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.strokeStyle = lightColor + '40';\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(history[0].x, history[0].y);\n\n for (let i = 1; i < history.length; i++) {\n ctx.lineTo(history[i].x, history[i].y);\n }\n ctx.stroke();\n }\n\n ctx.fillStyle = lightColor;\n ctx.beginPath();\n ctx.arc(head.x, head.y, 3, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const drawExplosions = (ctx: CanvasRenderingContext2D) => {\n for (const explosion of explosionsRef.current) {\n if (explosion.opacity <= 0) continue;\n\n const alpha = Math.floor(explosion.opacity * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.strokeStyle = lightColor + alpha;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2);\n ctx.stroke();\n }\n };\n\n const updateExplosions = () => {\n for (const explosion of explosionsRef.current) {\n explosion.radius += 0.5;\n explosion.opacity -= 0.08;\n }\n explosionsRef.current = explosionsRef.current.filter(\n (e) => e.opacity > 0\n );\n };\n\n const createExplosion = (x: number, y: number) => {\n explosionsRef.current.push({\n x,\n y,\n radius: 2,\n maxRadius: 8,\n opacity: 1,\n });\n };\n\n const createNodeGlow = (x: number, y: number) => {\n nodeGlowsRef.current.push({\n x,\n y,\n opacity: 1,\n });\n };\n\n const drawNodeGlows = (ctx: CanvasRenderingContext2D) => {\n for (const glow of nodeGlowsRef.current) {\n if (glow.opacity <= 0) continue;\n\n const alpha = Math.floor(glow.opacity * 0.4 * 255)\n .toString(16)\n .padStart(2, '0');\n ctx.fillStyle = lightColor + alpha;\n ctx.beginPath();\n ctx.arc(glow.x, glow.y, 2, 0, Math.PI * 2);\n ctx.fill();\n }\n };\n\n const updateNodeGlows = () => {\n for (const glow of nodeGlowsRef.current) {\n glow.opacity -= trailFadeSpeed * 3;\n }\n nodeGlowsRef.current = nodeGlowsRef.current.filter((g) => g.opacity > 0);\n };\n\n const getRandomTravel = () => {\n return (\n Math.floor(Math.random() * (maxTravel - minTravel + 1)) + minTravel\n );\n };\n\n const makeEdgeKey = (n1: Node, n2: Node) => {\n const keys = [n1.key, n2.key].sort();\n return `${keys[0]}-${keys[1]}`;\n };\n\n const trySpawn = (fromTop: boolean): boolean => {\n const sourceNodes = fromTop\n ? topNodesRef.current\n : bottomNodesRef.current;\n const direction: 1 | -1 = fromTop ? 1 : -1;\n\n if (sourceNodes.length === 0) return false;\n\n const startNode =\n sourceNodes[Math.floor(Math.random() * sourceNodes.length)];\n const connectedNodes = getConnectedNodes(startNode);\n\n const preferredNodes = connectedNodes.filter((n) =>\n fromTop ? n.y > startNode.y : n.y < startNode.y\n );\n const targetNodes =\n preferredNodes.length > 0 ? preferredNodes : connectedNodes;\n\n const availableNodes = targetNodes.filter((n) => {\n const key = makeEdgeKey(startNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) return false;\n\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const edgeKeyStr = makeEdgeKey(startNode, targetNode);\n activeEdgesRef.current.add(edgeKeyStr);\n\n particlesRef.current.push({\n id: particleIdRef.current++,\n currentNode: startNode,\n targetNode,\n progress: 0,\n traveled: 0,\n maxTravel: getRandomTravel(),\n canSplit: true,\n dead: false,\n direction,\n history: [{ x: startNode.x, y: startNode.y }],\n });\n\n trailsRef.current.push({\n fromX: startNode.x,\n fromY: startNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKeyStr,\n });\n\n return true;\n };\n\n const spawnParticle = () => {\n if (bidirectional) {\n const fromTop = spawnFromTopRef.current;\n spawnFromTopRef.current = !spawnFromTopRef.current;\n\n if (!trySpawn(fromTop)) {\n trySpawn(!fromTop);\n }\n } else {\n trySpawn(true);\n }\n };\n\n const updateParticles = () => {\n const newParticles: Particle[] = [];\n const maxHistoryLength = 10;\n\n for (const particle of particlesRef.current) {\n if (particle.dead) continue;\n\n particle.progress += lightSpeed * 0.02;\n\n const currentX =\n particle.currentNode.x +\n (particle.targetNode.x - particle.currentNode.x) * particle.progress;\n const currentY =\n particle.currentNode.y +\n (particle.targetNode.y - particle.currentNode.y) * particle.progress;\n particle.history.push({ x: currentX, y: currentY });\n if (particle.history.length > maxHistoryLength) {\n particle.history.shift();\n }\n\n const trailIndex = trailsRef.current.findIndex(\n (t) =>\n t.fromX === particle.currentNode.x &&\n t.fromY === particle.currentNode.y &&\n t.toX === particle.targetNode.x &&\n t.toY === particle.targetNode.y &&\n t.progress < 1\n );\n if (trailIndex !== -1) {\n trailsRef.current[trailIndex].progress = Math.min(\n particle.progress,\n 1\n );\n }\n\n if (particle.progress >= 1) {\n createNodeGlow(particle.targetNode.x, particle.targetNode.y);\n particle.traveled++;\n\n if (particle.traveled >= particle.maxTravel) {\n const x = particle.targetNode.x;\n const y = particle.targetNode.y;\n createExplosion(x, y);\n particle.dead = true;\n continue;\n }\n\n const currentNode = particle.targetNode;\n const connectedNodes = getConnectedNodes(\n currentNode,\n particle.currentNode\n );\n\n const availableNodes = connectedNodes.filter((n) => {\n const key = makeEdgeKey(currentNode, n);\n return !activeEdgesRef.current.has(key);\n });\n\n if (availableNodes.length === 0) {\n createExplosion(currentNode.x, currentNode.y);\n particle.dead = true;\n continue;\n }\n\n const shouldSplit =\n particle.canSplit &&\n Math.random() < splitChance &&\n availableNodes.length >= 2;\n\n if (shouldSplit) {\n const shuffled = [...availableNodes].sort(\n () => Math.random() - 0.5\n );\n const target1 = shuffled[0];\n const target2 = shuffled[1];\n\n const edgeKey1 = makeEdgeKey(currentNode, target1);\n const edgeKey2 = makeEdgeKey(currentNode, target2);\n activeEdgesRef.current.add(edgeKey1);\n activeEdgesRef.current.add(edgeKey2);\n\n particle.currentNode = currentNode;\n particle.targetNode = target1;\n particle.progress = 0;\n particle.canSplit = false;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target1.x,\n toY: target1.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey1,\n });\n\n newParticles.push({\n id: particleIdRef.current++,\n currentNode: currentNode,\n targetNode: target2,\n progress: 0,\n traveled: particle.traveled,\n maxTravel: particle.maxTravel,\n canSplit: false,\n dead: false,\n direction: particle.direction,\n history: [{ x: currentNode.x, y: currentNode.y }],\n });\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: target2.x,\n toY: target2.y,\n progress: 0,\n opacity: 1,\n edgeKey: edgeKey2,\n });\n } else {\n const targetNode =\n availableNodes[Math.floor(Math.random() * availableNodes.length)];\n const newEdgeKey = makeEdgeKey(currentNode, targetNode);\n activeEdgesRef.current.add(newEdgeKey);\n\n particle.currentNode = currentNode;\n particle.targetNode = targetNode;\n particle.progress = 0;\n\n trailsRef.current.push({\n fromX: currentNode.x,\n fromY: currentNode.y,\n toX: targetNode.x,\n toY: targetNode.y,\n progress: 0,\n opacity: 1,\n edgeKey: newEdgeKey,\n });\n }\n }\n }\n\n particlesRef.current.push(...newParticles);\n particlesRef.current = particlesRef.current.filter((p) => !p.dead);\n };\n\n const updateTrails = () => {\n for (const trail of trailsRef.current) {\n if (trail.progress >= 1) {\n trail.opacity -= trailFadeSpeed;\n }\n }\n const fadedTrails = trailsRef.current.filter((t) => t.opacity <= 0);\n for (const trail of fadedTrails) {\n activeEdgesRef.current.delete(trail.edgeKey);\n }\n trailsRef.current = trailsRef.current.filter((t) => t.opacity > 0);\n };\n\n const animate = (timestamp: number) => {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const { width, height } = dimensionsRef.current;\n\n ctx.save();\n ctx.scale(dpr, dpr);\n drawGrid(ctx, width, height);\n\n if (animated) {\n if (timestamp - lastSpawnRef.current > spawnRate) {\n spawnParticle();\n lastSpawnRef.current = timestamp;\n }\n\n updateParticles();\n updateTrails();\n updateExplosions();\n updateNodeGlows();\n drawTrails(ctx);\n drawNodeGlows(ctx);\n drawParticles(ctx);\n drawExplosions(ctx);\n }\n\n ctx.restore();\n animationRef.current = requestAnimationFrame(animate);\n };\n\n const setup = () => {\n const { width, height } = container.getBoundingClientRect();\n dimensionsRef.current = { width, height };\n dpr = window.devicePixelRatio || 1;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n if (shape === 4) {\n buildSquareGraph(width, height);\n } else {\n buildHexGraph(width, height);\n }\n\n particlesRef.current = [];\n trailsRef.current = [];\n activeEdgesRef.current.clear();\n explosionsRef.current = [];\n nodeGlowsRef.current = [];\n };\n\n const resizeObserver = new ResizeObserver(() => {\n setup();\n });\n\n resizeObserver.observe(container);\n setup();\n animationRef.current = requestAnimationFrame(animate);\n\n return () => {\n resizeObserver.disconnect();\n cancelAnimationFrame(animationRef.current);\n };\n }, [\n shape,\n cellSize,\n lineColor,\n lineWidth,\n animated,\n lightColor,\n lightSpeed,\n minTravel,\n maxTravel,\n spawnRate,\n splitChance,\n trailFadeSpeed,\n bidirectional,\n buildSquareGraph,\n buildHexGraph,\n getConnectedNodes,\n ]);\n\n return (\n <div ref={containerRef} className={`relative w-full h-full ${className}`}>\n <canvas ref={canvasRef} className=\"absolute inset-0\" />\n </div>\n );\n}\n\nexport default Grid;\n"],"mappings":"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","names":[]}
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.1",
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",