3d Cursor Effect
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
1// @ts-nocheck2'use client';34import { useState, useEffect } from 'react';5import { useMouse } from '@/hooks/use-mouse';67const MultiCursor = () => {8const [mouseState, ref] = useMouse();9const [cursors, setCursors] = useState([]);10const [pattern, setPattern] = useState('circle');11const [rotation, setRotation] = useState(0);1213useEffect(() => {14const interval = setInterval(() => {15setRotation((prev) => (prev + 2) % 360);16}, 16);17return () => clearInterval(interval);18}, []);1920useEffect(() => {21if (mouseState.x && mouseState.y) {22const patterns = {23circle: Array(8)24.fill(0)25.map((_, i) => ({26x:27Math.cos((i * Math.PI * 2) / 8 + (rotation * Math.PI) / 180) * 50,28y:29Math.sin((i * Math.PI * 2) / 8 + (rotation * Math.PI) / 180) * 50,30scale: 0.5 + Math.sin((rotation * Math.PI) / 180 + i) * 0.2,31})),32spiral: Array(12)33.fill(0)34.map((_, i) => {35const angle = (i * Math.PI * 2) / 12 + (rotation * Math.PI) / 180;36const radius = i * 5;37return {38x: Math.cos(angle) * radius,39y: Math.sin(angle) * radius,40scale: 1 - i * 0.05,41};42}),43grid: Array(9)44.fill(0)45.map((_, i) => ({46x: ((i % 3) - 1) * 40 * Math.cos((rotation * Math.PI) / 180),47y:48(Math.floor(i / 3) - 1) *4940 *50Math.sin((rotation * Math.PI) / 180),51scale: 0.5 + Math.sin((rotation * Math.PI) / 180 + i) * 0.2,52})),53};5455setCursors(patterns[pattern]);56}57}, [mouseState, pattern, rotation]);5859return (60<div className='relative w-full h-full' ref={ref}>61{mouseState.x !== null && mouseState.y !== null && (62<>63{/* Multi-cursors */}64{cursors.map((cursor, index) => (65<div66key={index}67className='fixed pointer-events-none transition-all duration-200'68style={{69left: mouseState.x + cursor.x,70top: mouseState.y + cursor.y,71transform: `translate(-50%, -50%) scale(${cursor.scale})`,72}}73>74<div75className='w-6 h-6 rounded-full mix-blend-screen'76style={{77background: `hsl(${(index * 30 + rotation) % 360}, 100%, 70%)`,78}}79/>80</div>81))}8283{/* Main cursor */}84<div85className='fixed pointer-events-none z-50'86style={{87left: mouseState.x,88top: mouseState.y,89transform: 'translate(-50%, -50%)',90}}91>92<div className='w-8 h-8 bg-white rounded-full mix-blend-screen' />93</div>94</>95)}9697<div className='flex flex-col items-center justify-center h-full gap-6'>98{['circle', 'spiral', 'grid'].map((patternType) => (99<button100key={patternType}101className={`px-6 py-3 rounded-lg transition-all duration-300 ${102pattern === patternType103? 'bg-purple-600 text-white'104: 'bg-purple-600/30 text-purple-200'105}`}106onClick={() => setPattern(patternType)}107>108{patternType.charAt(0).toUpperCase() + patternType.slice(1)} Pattern109</button>110))}111</div>112</div>113);114};115116export default MultiCursor;117
1'use client';2import { type RefObject, useLayoutEffect, useRef, useState } from 'react';34interface MouseState {5x: number | null;6y: number | null;7elementX: number | null;8elementY: number | null;9elementPositionX: number | null;10elementPositionY: number | null;11}1213export function useMouse(): [MouseState, RefObject<HTMLDivElement>] {14const [state, setState] = useState<MouseState>({15x: null,16y: null,17elementX: null,18elementY: null,19elementPositionX: null,20elementPositionY: null,21});2223const ref = useRef<HTMLDivElement | null>(null);2425useLayoutEffect(() => {26const handleMouseMove = (event: MouseEvent) => {27const newState: Partial<MouseState> = {28x: event.pageX,29y: event.pageY,30};3132if (ref.current instanceof Element) {33const { left, top } = ref.current.getBoundingClientRect();34const elementPositionX = left + window.scrollX;35const elementPositionY = top + window.scrollY;36const elementX = event.pageX - elementPositionX;37const elementY = event.pageY - elementPositionY;3839newState.elementX = elementX;40newState.elementY = elementY;41newState.elementPositionX = elementPositionX;42newState.elementPositionY = elementPositionY;43}4445setState((s) => ({46...s,47...newState,48}));49};5051document.addEventListener('mousemove', handleMouseMove);5253return () => {54document.removeEventListener('mousemove', handleMouseMove);55};56}, []);5758return [state, ref];59}