diff --git a/src/components/effects/ink-data-morph.tsx b/src/components/effects/ink-data-morph.tsx index 12bf29c..0667d40 100644 --- a/src/components/effects/ink-data-morph.tsx +++ b/src/components/effects/ink-data-morph.tsx @@ -465,6 +465,67 @@ function drawParticle( } } +function drawInkStrings( + ctx: CanvasRenderingContext2D, + particles: Particle[], + config: ResponsiveConfig, +): void { + const spreading = particles.filter( + p => p.phase === 'spreading' && p.age >= p.delay + ); + if (spreading.length < 2) { + return; + } + + const threshold = config.connectionDistance * config.inkStringThreshold; + + ctx.save(); + ctx.lineCap = 'round'; + for (let i = 0; i < spreading.length; i++) { + const a = spreading[i]; + if (!a) { + continue; + } + for (let j = i + 1; j < spreading.length; j++) { + const b = spreading[j]; + if (!b) { + continue; + } + const dx = a.x - b.x; + const dy = a.y - b.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < threshold && dist > 0) { + const distRatio = 1 - dist / threshold; + const alpha = distRatio * 0.15 * Math.min(a.opacity, b.opacity); + + const useAccent = ACCENT_TONES.includes(a.toneIndex) || ACCENT_TONES.includes(b.toneIndex); + const rgb = useAccent ? TONES[3].rgb : TONES[0].rgb; + + const mx = (a.x + b.x) / 2; + const my = (a.y + b.y) / 2; + const perpX = -dy / dist; + const perpY = dx / dist; + const curvature = (Math.random() - 0.5) * dist * 0.2; + const cpx = mx + perpX * curvature; + const cpy = my + perpY * curvature; + + const grad = ctx.createLinearGradient(a.x, a.y, b.x, b.y); + grad.addColorStop(0, `rgba(${rgb}, ${alpha * 0.3})`); + grad.addColorStop(0.5, `rgba(${rgb}, ${alpha})`); + grad.addColorStop(1, `rgba(${rgb}, ${alpha * 0.3})`); + + ctx.beginPath(); + ctx.moveTo(a.x, a.y); + ctx.quadraticCurveTo(cpx, cpy, b.x, b.y); + ctx.strokeStyle = grad; + ctx.lineWidth = Math.max(0.2, distRatio * 1.5); + ctx.stroke(); + } + } + } + ctx.restore(); +} + function drawConnections( ctx: CanvasRenderingContext2D, particles: Particle[], @@ -683,6 +744,7 @@ export function InkDataMorph({ } drawConnections(ctx, finalParticles, config.connectionDistance); + drawInkStrings(ctx, finalParticles, config); }, []); useEffect(() => { @@ -746,6 +808,7 @@ export function InkDataMorph({ } drawConnections(ctx, particles, config.connectionDistance); + drawInkStrings(ctx, particles, config); if (allComplete) { animationRef.current = undefined;