{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "liquid-cursor-shadcnui", "type": "registry:ui", "title": "Liquid Cursor", "description": "A liquid-like cursor effect that mixes colors on movement", "dependencies": [ "framer-motion", "react" ], "files": [ { "path": "@uitripled/react-shadcn/src/components/liquid-cursor.tsx", "content": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\ninterface Point {\n x: number;\n y: number;\n vx: number;\n vy: number;\n life: number;\n maxLife: number;\n radius: number;\n color: string;\n}\n\nconst COLORS = [\n \"#FF0080\", // Pink\n \"#7928CA\", // Purple\n \"#0070F3\", // Blue\n \"#00DFD8\", // Cyan\n \"#FF4D4D\", // Red\n \"#FFD700\", // Gold\n];\n\nexport const LiquidCursor = () => {\n const canvasRef = useRef(null);\n const pointsRef = useRef([]);\n const mouseRef = useRef({ x: 0, y: 0, lastX: 0, lastY: 0, moved: false });\n const rafRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const resize = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n window.addEventListener(\"resize\", resize);\n resize();\n\n const handleMouseMove = (e: MouseEvent) => {\n mouseRef.current.x = e.clientX;\n mouseRef.current.y = e.clientY;\n mouseRef.current.moved = true;\n };\n window.addEventListener(\"mousemove\", handleMouseMove);\n\n const addPoint = (x: number, y: number) => {\n const angle = Math.random() * Math.PI * 2;\n const speed = Math.random() * 0.5;\n const color = COLORS[Math.floor(Math.random() * COLORS.length)];\n\n pointsRef.current.push({\n x,\n y,\n vx: Math.cos(angle) * speed,\n vy: Math.sin(angle) * speed,\n life: 1,\n maxLife: 1,\n radius: Math.random() * 20 + 10, // Random radius between 10 and 30\n color,\n });\n };\n\n const animate = () => {\n // Clear with a slight fade for trails, or full clear\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n // Add new points if mouse moved\n if (mouseRef.current.moved) {\n const dist = Math.hypot(\n mouseRef.current.x - mouseRef.current.lastX,\n mouseRef.current.y - mouseRef.current.lastY\n );\n\n // Interpolate points for smooth lines\n if (dist > 0) {\n const steps = Math.min(dist, 20); // Limit steps to avoid lag\n for (let i = 0; i < steps; i += 2) {\n const t = i / steps;\n const x =\n mouseRef.current.lastX +\n (mouseRef.current.x - mouseRef.current.lastX) * t;\n const y =\n mouseRef.current.lastY +\n (mouseRef.current.y - mouseRef.current.lastY) * t;\n // Only add points occasionally to avoid overcrowding\n if (Math.random() > 0.5) addPoint(x, y);\n }\n }\n\n mouseRef.current.lastX = mouseRef.current.x;\n mouseRef.current.lastY = mouseRef.current.y;\n mouseRef.current.moved = false; // Reset moved flag until next event\n } else {\n // If mouse stopped, we update last position to current to be safe\n mouseRef.current.lastX = mouseRef.current.x;\n mouseRef.current.lastY = mouseRef.current.y;\n }\n\n // Update and draw points\n for (let i = pointsRef.current.length - 1; i >= 0; i--) {\n const p = pointsRef.current[i];\n\n p.life -= 0.01; // Decay rate\n p.x += p.vx;\n p.y += p.vy;\n p.radius *= 0.99; // Shrink slightly\n\n if (p.life <= 0 || p.radius < 0.5) {\n pointsRef.current.splice(i, 1);\n continue;\n }\n\n ctx.beginPath();\n ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);\n ctx.fillStyle = p.color;\n // Use globalAlpha for fading\n ctx.globalAlpha = p.life;\n ctx.fill();\n ctx.globalAlpha = 1;\n }\n\n rafRef.current = requestAnimationFrame(animate);\n };\n\n animate();\n\n return () => {\n window.removeEventListener(\"resize\", resize);\n window.removeEventListener(\"mousemove\", handleMouseMove);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, []);\n\n return (\n
\n {/* SVG Filter for the Gooey Effect */}\n \n \n \n \n \n \n \n \n \n\n \n
\n );\n};\n", "type": "registry:ui", "target": "components/uitripled/liquid-cursor-shadcnui.tsx" } ] }