Skip to content

Commit f781b29

Browse files
Merge pull request #114 from TCP-Tech/dev
Dev
2 parents 1a89c1c + a8ac6be commit f781b29

5 files changed

Lines changed: 1308 additions & 718 deletions

File tree

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"gsap": "^3.13.0",
2929
"leaflet": "^1.9.4",
3030
"lenis": "^1.3.11",
31+
"lucide-react": "^0.546.0",
3132
"motion": "^12.23.12",
3233
"react": "^19.1.0",
3334
"react-chartjs-2": "^5.3.0",

src/components/cursor/Cursor.jsx

Lines changed: 148 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,166 @@
1-
import React, { useState, useEffect, useRef } from 'react';
1+
import React, { useState, useEffect, useRef } from "react";
22

33
const SimpleCarnivalCursor = ({ children }) => {
4-
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
5-
const [isHovering, setIsHovering] = useState(false);
6-
const [isClicking, setIsClicking] = useState(false);
7-
const [isMobile, setIsMobile] = useState(false);
8-
const [sparks, setSparks] = useState([]);
9-
const sparksRef = useRef([]);
10-
const animationRef = useRef();
11-
12-
class Spark {
13-
constructor(x, y) {
14-
this.id = Math.random();
15-
this.x = x + (Math.random() - 0.5) * 20;
16-
this.y = y + (Math.random() - 0.5) * 20;
17-
this.vx = (Math.random() - 0.5) * 3;
18-
this.vy = (Math.random() - 0.5) * 3;
19-
this.life = 1;
20-
this.decay = Math.random() * 0.02 + 0.02;
21-
this.size = Math.random() * 3 + 2;
22-
this.hue = Math.random() * 60 + 15; // Yellow to orange range
4+
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
5+
const [isHovering, setIsHovering] = useState(false);
6+
const [isClicking, setIsClicking] = useState(false);
7+
const [isMobile, setIsMobile] = useState(false);
8+
const [sparks, setSparks] = useState([]);
9+
const sparksRef = useRef([]);
10+
const animationRef = useRef();
11+
12+
class Spark {
13+
constructor(x, y) {
14+
this.id = Math.random();
15+
this.x = x + (Math.random() - 0.5) * 20;
16+
this.y = y + (Math.random() - 0.5) * 20;
17+
this.vx = (Math.random() - 0.5) * 3;
18+
this.vy = (Math.random() - 0.5) * 3;
19+
this.life = 1;
20+
this.decay = Math.random() * 0.02 + 0.02;
21+
this.size = Math.random() * 3 + 2;
22+
this.hue = Math.random() * 60 + 15; // Yellow to orange range
23+
}
24+
25+
update() {
26+
this.x += this.vx;
27+
this.y += this.vy;
28+
this.life -= this.decay;
29+
this.vx *= 0.99;
30+
this.vy *= 0.99;
31+
}
2332
}
2433

25-
update() {
26-
this.x += this.vx;
27-
this.y += this.vy;
28-
this.life -= this.decay;
29-
this.vx *= 0.99;
30-
this.vy *= 0.99;
34+
useEffect(() => {
35+
// Check if it's a mobile device
36+
setIsMobile("ontouchstart" in window);
37+
38+
if (isMobile) return;
39+
40+
const handleMouseMove = (e) => {
41+
setMousePos({ x: e.clientX, y: e.clientY });
42+
43+
// Add sparks on mouse move
44+
if (Math.random() < 0.7) {
45+
sparksRef.current.push(new Spark(e.clientX, e.clientY));
46+
}
47+
};
48+
49+
const handleMouseOver = (e) => {
50+
const isInteractive = e.target.closest(
51+
'button, a, input, textarea, select, [role="button"]'
52+
);
53+
setIsHovering(!!isInteractive);
54+
};
55+
56+
const handleMouseDown = () => {
57+
setIsClicking(true);
58+
// Add more sparks on click
59+
for (let i = 0; i < 8; i++) {
60+
sparksRef.current.push(new Spark(mousePos.x, mousePos.y));
61+
}
62+
};
63+
64+
const handleMouseUp = () => setIsClicking(false);
65+
66+
const animateSparks = () => {
67+
sparksRef.current = sparksRef.current.filter((spark) => {
68+
spark.update();
69+
return spark.life > 0;
70+
});
71+
72+
setSparks([...sparksRef.current]);
73+
animationRef.current = requestAnimationFrame(animateSparks);
74+
};
75+
76+
document.addEventListener("mousemove", handleMouseMove);
77+
document.addEventListener("mouseover", handleMouseOver);
78+
document.addEventListener("mousedown", handleMouseDown);
79+
document.addEventListener("mouseup", handleMouseUp);
80+
81+
animateSparks();
82+
83+
return () => {
84+
document.removeEventListener("mousemove", handleMouseMove);
85+
document.removeEventListener("mouseover", handleMouseOver);
86+
document.removeEventListener("mousedown", handleMouseDown);
87+
document.removeEventListener("mouseup", handleMouseUp);
88+
if (animationRef.current) {
89+
cancelAnimationFrame(animationRef.current);
90+
}
91+
};
92+
}, [isMobile, mousePos.x, mousePos.y]);
93+
94+
if (isMobile) {
95+
return <div>{children}</div>;
3196
}
32-
}
33-
34-
useEffect(() => {
35-
// Check if it's a mobile device
36-
setIsMobile('ontouchstart' in window);
37-
38-
if (isMobile) return;
39-
40-
const handleMouseMove = (e) => {
41-
setMousePos({ x: e.clientX, y: e.clientY });
42-
43-
// Add sparks on mouse move
44-
if (Math.random() < 0.7) {
45-
sparksRef.current.push(new Spark(e.clientX, e.clientY));
46-
}
47-
};
48-
49-
const handleMouseOver = (e) => {
50-
const isInteractive = e.target.closest('button, a, input, textarea, select, [role="button"]');
51-
setIsHovering(!!isInteractive);
52-
};
53-
54-
const handleMouseDown = () => {
55-
setIsClicking(true);
56-
// Add more sparks on click
57-
for (let i = 0; i < 8; i++) {
58-
sparksRef.current.push(new Spark(mousePos.x, mousePos.y));
59-
}
60-
};
61-
62-
const handleMouseUp = () => setIsClicking(false);
63-
64-
const animateSparks = () => {
65-
sparksRef.current = sparksRef.current.filter(spark => {
66-
spark.update();
67-
return spark.life > 0;
68-
});
69-
70-
setSparks([...sparksRef.current]);
71-
animationRef.current = requestAnimationFrame(animateSparks);
72-
};
73-
74-
document.addEventListener('mousemove', handleMouseMove);
75-
document.addEventListener('mouseover', handleMouseOver);
76-
document.addEventListener('mousedown', handleMouseDown);
77-
document.addEventListener('mouseup', handleMouseUp);
78-
79-
animateSparks();
80-
81-
return () => {
82-
document.removeEventListener('mousemove', handleMouseMove);
83-
document.removeEventListener('mouseover', handleMouseOver);
84-
document.removeEventListener('mousedown', handleMouseDown);
85-
document.removeEventListener('mouseup', handleMouseUp);
86-
if (animationRef.current) {
87-
cancelAnimationFrame(animationRef.current);
88-
}
89-
};
90-
}, [isMobile, mousePos.x, mousePos.y]);
91-
92-
if (isMobile) {
93-
return <div>{children}</div>;
94-
}
95-
96-
return (
97-
<>
98-
{/* Hide default cursor completely */}
99-
<style jsx>{`
97+
98+
return (
99+
<>
100+
{/* Hide default cursor completely */}
101+
<style>{`
100102
*, *::before, *::after {
101103
cursor: none !important;
102104
}
103105
`}</style>
104-
105-
<div className="cursor-none">
106-
{children}
107-
</div>
108-
109-
{/* Sparks */}
110-
{sparks.map(spark => (
111-
<div
112-
key={spark.id}
113-
className="fixed pointer-events-none z-[9998] rounded-full"
114-
style={{
115-
left: spark.x - spark.size/2,
116-
top: spark.y - spark.size/2,
117-
width: spark.size,
118-
height: spark.size,
119-
opacity: spark.life,
120-
background: `hsl(${spark.hue}, 100%, 60%)`,
121-
boxShadow: `0 0 ${spark.size * 2}px hsl(${spark.hue}, 100%, 60%)`,
122-
transform: `scale(${spark.life})`,
123-
}}
124-
/>
125-
))}
126-
127-
{/* Main cursor with glow effect */}
128-
<div
129-
className="fixed pointer-events-none z-[9999] transition-transform duration-100 ease-out"
130-
style={{
131-
left: mousePos.x - 8,
132-
top: mousePos.y - 8,
133-
transform: `scale(${isClicking ? 1.5 : isHovering ? 1.2 : 1})`,
134-
}}
135-
>
136-
<div
137-
className={`
106+
107+
<div className="cursor-none">{children}</div>
108+
109+
{/* Sparks */}
110+
{sparks.map((spark) => (
111+
<div
112+
key={spark.id}
113+
className="fixed pointer-events-none z-[9998] rounded-full"
114+
style={{
115+
left: spark.x - spark.size / 2,
116+
top: spark.y - spark.size / 2,
117+
width: spark.size,
118+
height: spark.size,
119+
opacity: spark.life,
120+
background: `hsl(${spark.hue}, 100%, 60%)`,
121+
boxShadow: `0 0 ${spark.size * 2}px hsl(${
122+
spark.hue
123+
}, 100%, 60%)`,
124+
transform: `scale(${spark.life})`,
125+
}}
126+
/>
127+
))}
128+
129+
{/* Main cursor with glow effect */}
130+
<div
131+
className="fixed pointer-events-none z-[9999] transition-transform duration-100 ease-out"
132+
style={{
133+
left: mousePos.x - 8,
134+
top: mousePos.y - 8,
135+
transform: `scale(${
136+
isClicking ? 1.5 : isHovering ? 1.2 : 1
137+
})`,
138+
}}
139+
>
140+
<div
141+
className={`
138142
w-4 h-4 rounded-full
139143
transition-all duration-200 ease-out
140-
${isHovering ? 'animate-pulse' : ''}
141-
${isClicking ? 'animate-ping' : ''}
144+
${isHovering ? "animate-pulse" : ""}
145+
${isClicking ? "animate-ping" : ""}
142146
`}
143-
style={{
144-
background: isClicking
145-
? 'radial-gradient(circle, #ffffff 0%, #ffff00 50%, #ff6600 100%)'
146-
: isHovering
147-
? 'radial-gradient(circle, #ffffff 0%, #ffaa00 70%, #ff4400 100%)'
148-
: 'radial-gradient(circle, #ffffff 0%, #ffdd00 60%, #ff8800 100%)',
149-
boxShadow: `
147+
style={{
148+
background: isClicking
149+
? "radial-gradient(circle, #ffffff 0%, #ffff00 50%, #ff6600 100%)"
150+
: isHovering
151+
? "radial-gradient(circle, #ffffff 0%, #ffaa00 70%, #ff4400 100%)"
152+
: "radial-gradient(circle, #ffffff 0%, #ffdd00 60%, #ff8800 100%)",
153+
boxShadow: `
150154
0 0 20px rgba(255, 255, 255, 0.8),
151155
0 0 40px rgba(255, 200, 0, 0.6),
152156
0 0 60px rgba(255, 100, 0, 0.4)
153-
${isClicking ? ', 0 0 80px rgba(255, 255, 255, 0.9)' : ''}
157+
${isClicking ? ", 0 0 80px rgba(255, 255, 255, 0.9)" : ""}
154158
`,
155-
}}
156-
/>
157-
</div>
158-
</>
159-
);
159+
}}
160+
/>
161+
</div>
162+
</>
163+
);
160164
};
161165

162-
export default SimpleCarnivalCursor;
166+
export default SimpleCarnivalCursor;

0 commit comments

Comments
 (0)