diff --git a/components/agents/AgentWorkflowParticleScatter.tsx b/components/agents/AgentWorkflowParticleScatter.tsx index 92e708d37..25b6b027c 100644 --- a/components/agents/AgentWorkflowParticleScatter.tsx +++ b/components/agents/AgentWorkflowParticleScatter.tsx @@ -1,197 +1,14 @@ "use client"; -import { useEffect, useRef } from "react"; import { cn } from "@/lib/utils"; type AgentWorkflowParticleScatterProps = { className?: string; - density?: "low" | "medium" | "high"; }; -type Particle = { - x: number; - y: number; - radius: number; - speed: number; - drift: number; - alpha: number; - hue: number; - phase: number; - swing: number; - depth: number; -}; - -const PARTICLE_DENSITY_MULTIPLIER = { - low: 0.75, - medium: 1, - high: 1.35, -} as const; - -function randomBetween(min: number, max: number) { - return min + Math.random() * (max - min); -} - export function AgentWorkflowParticleScatter({ className, - density = "medium", }: AgentWorkflowParticleScatterProps) { - const canvasRef = useRef(null); - - useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) { - return; - } - - const context = canvas.getContext("2d"); - if (!context) { - return; - } - - const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); - let width = 0; - let height = 0; - let animationFrameId = 0; - let particles: Particle[] = []; - - const buildParticle = (): Particle => { - const depth = randomBetween(0.65, 1.15); - const spread = width * randomBetween(0.16, 0.48); - const originX = width / 2 + randomBetween(-spread, spread); - const originY = height * randomBetween(0.56, 1.06); - - return { - x: originX, - y: originY, - radius: randomBetween(0.9, 2.8) * depth, - speed: randomBetween(0.08, 0.34) * depth, - drift: randomBetween(0.12, 0.38) * (Math.random() > 0.5 ? 1 : -1), - alpha: randomBetween(0.16, 0.84), - hue: Math.random() > 0.7 ? 0 : 160, - phase: randomBetween(0, Math.PI * 2), - swing: randomBetween(10, 42), - depth, - }; - }; - - const resetParticle = (particle: Particle, respawnNearBottom = false) => { - const next = buildParticle(); - particle.x = next.x; - particle.y = respawnNearBottom ? height * randomBetween(0.72, 1.04) : next.y; - particle.radius = next.radius; - particle.speed = next.speed; - particle.drift = next.drift; - particle.alpha = next.alpha; - particle.hue = next.hue; - particle.phase = next.phase; - particle.swing = next.swing; - particle.depth = next.depth; - }; - - const resize = () => { - const bounds = canvas.getBoundingClientRect(); - const pixelRatio = Math.min(window.devicePixelRatio || 1, 2); - width = Math.max(1, bounds.width); - height = Math.max(1, bounds.height); - canvas.width = Math.round(width * pixelRatio); - canvas.height = Math.round(height * pixelRatio); - context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); - - const baseCount = Math.round((width / 12) * PARTICLE_DENSITY_MULTIPLIER[density]); - particles = Array.from( - { length: Math.max(56, Math.min(220, baseCount)) }, - () => buildParticle() - ); - }; - - const drawGlow = (x: number, y: number, radius: number, alpha: number, hue: number) => { - const gradient = context.createRadialGradient(x, y, 0, x, y, radius * 5.5); - if (hue === 160) { - gradient.addColorStop(0, `rgba(110, 255, 217, ${alpha})`); - gradient.addColorStop(0.35, `rgba(26, 214, 168, ${alpha * 0.7})`); - gradient.addColorStop(1, "rgba(26, 214, 168, 0)"); - } else { - gradient.addColorStop(0, `rgba(255, 255, 255, ${alpha})`); - gradient.addColorStop(0.42, `rgba(209, 255, 244, ${alpha * 0.45})`); - gradient.addColorStop(1, "rgba(255, 255, 255, 0)"); - } - context.fillStyle = gradient; - context.beginPath(); - context.arc(x, y, radius * 5.5, 0, Math.PI * 2); - context.fill(); - }; - - const render = (time: number) => { - context.clearRect(0, 0, width, height); - - const bottomGlow = context.createRadialGradient( - width / 2, - height * 0.92, - 0, - width / 2, - height * 0.92, - width * 0.42 - ); - bottomGlow.addColorStop(0, "rgba(19, 221, 179, 0.18)"); - bottomGlow.addColorStop(0.5, "rgba(19, 221, 179, 0.08)"); - bottomGlow.addColorStop(1, "rgba(19, 221, 179, 0)"); - context.fillStyle = bottomGlow; - context.fillRect(0, 0, width, height); - - particles.forEach((particle) => { - particle.phase += 0.012; - particle.y -= particle.speed; - particle.x += Math.sin(time * 0.00032 + particle.phase) * particle.drift; - - const waveOffset = - Math.sin(time * 0.001 + particle.phase * 2.4) * particle.swing * 0.018; - const pulseAlpha = - particle.alpha * (0.7 + 0.3 * Math.sin(time * 0.0016 + particle.phase)); - const renderX = particle.x + waveOffset; - const renderY = particle.y; - - if (renderY < -24 || renderX < -32 || renderX > width + 32) { - resetParticle(particle, true); - } - - drawGlow(renderX, renderY, particle.radius, pulseAlpha * 0.68, particle.hue); - - context.beginPath(); - context.fillStyle = - particle.hue === 160 - ? `rgba(155, 255, 230, ${pulseAlpha})` - : `rgba(255, 255, 255, ${pulseAlpha})`; - context.arc(renderX, renderY, particle.radius, 0, Math.PI * 2); - context.fill(); - }); - - if (!reducedMotionQuery.matches) { - animationFrameId = window.requestAnimationFrame(render); - } - }; - - resize(); - - const handleResize = () => { - resize(); - }; - - window.addEventListener("resize", handleResize); - - if (reducedMotionQuery.matches) { - render(0); - } else { - animationFrameId = window.requestAnimationFrame(render); - } - - return () => { - window.removeEventListener("resize", handleResize); - if (animationFrameId) { - window.cancelAnimationFrame(animationFrameId); - } - }; - }, [density]); - return (
-
); diff --git a/components/agents/AgentWorkflowShowcaseBanner.tsx b/components/agents/AgentWorkflowShowcaseBanner.tsx index 27c1b150d..7a785b1df 100644 --- a/components/agents/AgentWorkflowShowcaseBanner.tsx +++ b/components/agents/AgentWorkflowShowcaseBanner.tsx @@ -44,9 +44,8 @@ export function AgentWorkflowShowcaseBanner({ animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} > - -
-
+ +

{resolved.title}

@@ -116,7 +115,6 @@ export function AgentWorkflowShowcaseBanner({
- {resolved.overlayText ? (
diff --git a/public/particle-scatter-effect.html b/public/particle-scatter-effect.html new file mode 100644 index 000000000..c5a8caf10 --- /dev/null +++ b/public/particle-scatter-effect.html @@ -0,0 +1,299 @@ + + + + + + Neural Particle - Scatter Effect + + + + +
+ + + \ No newline at end of file