diff --git a/src/components/effects/ink-data-morph.tsx b/src/components/effects/ink-data-morph.tsx index 016fec3..12bf29c 100644 --- a/src/components/effects/ink-data-morph.tsx +++ b/src/components/effects/ink-data-morph.tsx @@ -189,6 +189,8 @@ function updateParticle(p: Particle, maxTrail: number): void { } } + p.prevVx = p.vx; + p.prevVy = p.vy; p.x += p.vx; p.y += p.vy; p.vx *= 0.98; @@ -377,6 +379,43 @@ function drawInkLayers( } } +function drawFeibai( + ctx: CanvasRenderingContext2D, + p: Particle, + rgb: string, +): void { + const speed = Math.sqrt(p.prevVx * p.prevVx + p.prevVy * p.prevVy); + if (speed < 0.1) { + return; + } + + const dirX = -p.prevVx / speed; + const dirY = -p.prevVy / speed; + const length = p.radius * (3 + Math.random() * 2); + const endX = p.x + dirX * length; + const endY = p.y + dirY * length; + + const perpX = -dirY; + const perpY = dirX; + const curvature = (Math.random() - 0.5) * length * 0.15; + const cpX = (p.x + endX) / 2 + perpX * curvature; + const cpY = (p.y + endY) / 2 + perpY * curvature; + + const grad = ctx.createLinearGradient(p.x, p.y, endX, endY); + grad.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.25})`); + grad.addColorStop(1, 'transparent'); + + ctx.save(); + ctx.lineCap = 'round'; + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + ctx.quadraticCurveTo(cpX, cpY, endX, endY); + ctx.strokeStyle = grad; + ctx.lineWidth = Math.max(0.3, p.radius * 0.6); + ctx.stroke(); + ctx.restore(); +} + function drawParticle( ctx: CanvasRenderingContext2D, p: Particle, @@ -400,6 +439,9 @@ function drawParticle( drawInkLayers(ctx, p, config); } else if (p.phase === 'settling') { drawInkLayers(ctx, p, config); + if (config.showFeibai) { + drawFeibai(ctx, p, rgb); + } } else if (p.phase === 'fading') { const grad1 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r * 1.5); grad1.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.6})`);