Gradient Cursor Effect

An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.

1
// @ts-nocheck
2
3
'use client';
4
5
import { useState, useEffect } from 'react';
6
import { motion, AnimatePresence } from 'framer-motion';
7
import { useMouse } from '@/hooks/use-mouse';
8
9
const GradientCursor = () => {
10
const [mouseState, ref] = useMouse();
11
const [hue, setHue] = useState(0);
12
13
const [particles, setParticles] = useState<
14
{ id: number; x: number; y: number; size: number; intensity: number }[]
15
>([]);
16
17
useEffect(() => {
18
if (mouseState.x && mouseState.y) {
19
const newHue = (mouseState.x || 0) % 360;
20
setHue(newHue);
21
22
// Add multiple new particles
23
const newParticles = Array.from({ length: 3 }, () => ({
24
id: Date.now() + Math.random(),
25
x: mouseState.x + (Math.random() - 0.5) * 20,
26
y: mouseState.y + (Math.random() - 0.5) * 20,
27
size: Math.random() * 3 + 2, // Random size between 2 and 5
28
intensity: Math.random() * 0.5 + 0.5, // Random intensity between 0.5 and 1
29
}));
30
31
setParticles((prev) => [...prev, ...newParticles].slice(-30)); // Keep last 30 particles
32
}
33
}, [mouseState.x, mouseState.y]);
34
35
return (
36
<div className='relative w-full h-full ' ref={ref}>
37
{mouseState.x !== null && mouseState.y !== null && (
38
<>
39
{/* Main cursor with gradient */}
40
<motion.div
41
className='fixed pointer-events-none z-50'
42
style={{
43
left: mouseState.x,
44
top: mouseState.y,
45
x: '-50%',
46
y: '-50%',
47
width: '40px',
48
height: '40px',
49
}}
50
transition={{ duration: 0.2 }}
51
>
52
<div
53
className='w-full h-full rounded-full mix-blend-screen'
54
style={{
55
background: `radial-gradient(
56
circle at center,
57
hsl(${hue}, 100%, 70%),
58
hsl(${(hue + 60) % 360}, 100%, 60%)
59
)`,
60
boxShadow: `0 0 20px hsl(${hue}, 100%, 50%, 0.5)`,
61
}}
62
/>
63
</motion.div>
64
65
{/* Particle trail */}
66
<AnimatePresence>
67
{particles.map((particle, index) => (
68
<motion.div
69
key={particle.id}
70
className='fixed pointer-events-none mix-blend-screen'
71
style={{
72
left: particle.x,
73
top: particle.y,
74
x: '-50%',
75
y: '-50%',
76
}}
77
initial={{ opacity: particle.intensity, scale: 0 }}
78
animate={{ opacity: 0, scale: particle.size }}
79
exit={{ opacity: 0 }}
80
transition={{ duration: 1, ease: 'easeOut' }}
81
>
82
<div
83
className='rounded-full'
84
style={{
85
width: `${particle.size * 4}px`,
86
height: `${particle.size * 4}px`,
87
background: `radial-gradient(
88
circle at center,
89
hsl(${(hue + index * 10) % 360}, 100%, ${70 + particle.intensity * 30}%),
90
transparent
91
)`,
92
filter: 'blur(2px)',
93
boxShadow: `0 0 ${particle.size * 2}px hsl(${(hue + index * 10) % 360}, 100%, 50%, ${particle.intensity})`,
94
}}
95
/>
96
</motion.div>
97
))}
98
</AnimatePresence>
99
</>
100
)}
101
</div>
102
);
103
};
104
105
export default GradientCursor;
106

Installtion

1
npm install framer-motion

useMouse

1
'use client';
2
import { type RefObject, useLayoutEffect, useRef, useState } from 'react';
3
4
interface MouseState {
5
x: number | null;
6
y: number | null;
7
elementX: number | null;
8
elementY: number | null;
9
elementPositionX: number | null;
10
elementPositionY: number | null;
11
}
12
13
export function useMouse(): [MouseState, RefObject<HTMLDivElement>] {
14
const [state, setState] = useState<MouseState>({
15
x: null,
16
y: null,
17
elementX: null,
18
elementY: null,
19
elementPositionX: null,
20
elementPositionY: null,
21
});
22
23
const ref = useRef<HTMLDivElement | null>(null);
24
25
useLayoutEffect(() => {
26
const handleMouseMove = (event: MouseEvent) => {
27
const newState: Partial<MouseState> = {
28
x: event.pageX,
29
y: event.pageY,
30
};
31
32
if (ref.current instanceof Element) {
33
const { left, top } = ref.current.getBoundingClientRect();
34
const elementPositionX = left + window.scrollX;
35
const elementPositionY = top + window.scrollY;
36
const elementX = event.pageX - elementPositionX;
37
const elementY = event.pageY - elementPositionY;
38
39
newState.elementX = elementX;
40
newState.elementY = elementY;
41
newState.elementPositionX = elementPositionX;
42
newState.elementPositionY = elementPositionY;
43
}
44
45
setState((s) => ({
46
...s,
47
...newState,
48
}));
49
};
50
51
document.addEventListener('mousemove', handleMouseMove);
52
53
return () => {
54
document.removeEventListener('mousemove', handleMouseMove);
55
};
56
}, []);
57
58
return [state, ref];
59
}