Skip to content

Commit de41000

Browse files
committed
feat: integrate custom Cursor component with trrail inprogress
1 parent 0d2f07b commit de41000

File tree

5 files changed

+173
-3
lines changed

5 files changed

+173
-3
lines changed

apps/collabydraw/components/Scale.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const Scale = ({
2020
};
2121

2222
return (
23-
<div className="ScaleBar ZoomINZoomOut_Bar Mobile_View fixed bottom-4 left-4 rounded-lg hidden md:flex items-center bg-white dark:bg-w-bg surface-box-shadow">
23+
<div className="ScaleBar ZoomINZoomOut_Bar Mobile_View fixed z-[4] bottom-4 left-4 rounded-lg hidden md:flex items-center bg-white dark:bg-w-bg surface-box-shadow">
2424
<TooltipProvider delayDuration={0}>
2525
<Tooltip>
2626
<TooltipTrigger asChild>

apps/collabydraw/components/canvas/CanvasSheet.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,12 @@ export function CanvasSheet({ roomName, roomId, userId, userName }: { roomName:
256256
}
257257

258258
return (
259-
<div className={`collabydraw h-screen overflow-hidden ${(activeTool === "grab" && !sidebarOpen) ? (grabbing ? "cursor-grabbing" : "cursor-grab") : "cursor-crosshair"} `}>
259+
<div className={cn("collabydraw h-screen overflow-hidden",
260+
activeTool === "eraser"
261+
? "cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAOBJREFUOE9jZKAyYKSyeQzDwMD////7MDAw6EGD5hIjI+MWfMGE08sggz5+/Dj71q1bHPv27eMFGeLk5PRZTU3tBz8/fyoug7EaCDLs58+fa0NDQ9k2b96M4iBfX1+G1atX/2JnZw/GZihWAz98+PA8NjZWAt0wmMkgQxcvXvxCQEBAEt37GAaCXHf69OnFZmZmAvjC6tSpUx9MTU1j0V2JzcCqzs7OpoqKCmZ8BnZ0dPwtLy+vY2RkbENWRxcDqetlkPOpGikgA6mebGCGUi1hI8ca1bIeucXaMCi+SPU6AHRTjhWg+vuGAAAAAElFTkSuQmCC')_10_10,auto]"
262+
: activeTool === "grab" && !sidebarOpen
263+
? grabbing ? "cursor-grabbing" : "cursor-grab"
264+
: "cursor-crosshair")}>
260265
<div className="App_Menu App_Menu_Top fixed z-[4] top-4 right-4 left-4 flex justify-center items-center md:grid md:grid-cols-[1fr_auto_1fr] md:gap-8 md:items-start">
261266
{matches && (
262267
<div className="Main_Menu_Stack Sidebar_Trigger_Button md:grid md:gap-[calc(.25rem*6)] grid-cols-[auto] grid-flow-row grid-rows auto-rows-min justify-self-start">

apps/collabydraw/components/canvas/StandaloneCanvas.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,13 @@ export function StandaloneCanvas() {
292292
};
293293

294294
return (
295-
<div data-isloading={isLoading} data-matches={matches} className={`collabydraw h-screen overflow-hidden ${(activeTool === "grab" && !sidebarOpen) ? (grabbing ? "cursor-grabbing" : "cursor-grab") : "cursor-crosshair"} `}>
295+
<div className={cn("collabydraw h-screen overflow-hidden",
296+
activeTool === "eraser"
297+
? "cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAOBJREFUOE9jZKAyYKSyeQzDwMD////7MDAw6EGD5hIjI+MWfMGE08sggz5+/Dj71q1bHPv27eMFGeLk5PRZTU3tBz8/fyoug7EaCDLs58+fa0NDQ9k2b96M4iBfX1+G1atX/2JnZw/GZihWAz98+PA8NjZWAt0wmMkgQxcvXvxCQEBAEt37GAaCXHf69OnFZmZmAvjC6tSpUx9MTU1j0V2JzcCqzs7OpoqKCmZ8BnZ0dPwtLy+vY2RkbENWRxcDqetlkPOpGikgA6mebGCGUi1hI8ca1bIeucXaMCi+SPU6AHRTjhWg+vuGAAAAAElFTkSuQmCC')_10_10,auto]"
298+
: activeTool === "grab" && !sidebarOpen
299+
? grabbing ? "cursor-grabbing" : "cursor-grab"
300+
: "cursor-crosshair")}>
301+
296302
{!isLoading && (
297303
<div className="App_Menu App_Menu_Top fixed z-[4] top-4 right-4 left-4 flex justify-center items-center md:grid md:grid-cols-[1fr_auto_1fr] md:gap-8 md:items-start">
298304
{matches && !isLoading && (
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"use client"
2+
3+
import { useTheme } from "next-themes"
4+
import { useEffect, useRef } from "react"
5+
6+
interface EraserCursorProps {
7+
size: number
8+
isActive: boolean
9+
}
10+
11+
export function EraserCursor({ size, isActive }: EraserCursorProps) {
12+
const { theme } = useTheme()
13+
const cursorRef = useRef<HTMLDivElement>(null)
14+
const trailRef = useRef<HTMLCanvasElement>(null)
15+
const positionRef = useRef({ x: 0, y: 0 })
16+
const prevPositionRef = useRef({ x: 0, y: 0 })
17+
const animationFrameRef = useRef<number | null>(null)
18+
19+
useEffect(() => {
20+
if (!isActive) return
21+
22+
const handleMouseMove = (e: MouseEvent) => {
23+
if (cursorRef.current) {
24+
const x = e.clientX
25+
const y = e.clientY
26+
cursorRef.current.style.transform = `translate(${x}px, ${y}px)`
27+
28+
prevPositionRef.current = { ...positionRef.current }
29+
positionRef.current = { x, y }
30+
}
31+
}
32+
33+
const drawTrail = () => {
34+
if (!trailRef.current || !isActive) return
35+
const canvas = trailRef.current
36+
const ctx = canvas.getContext("2d")
37+
if (!ctx) return
38+
39+
canvas.width = window.innerWidth
40+
canvas.height = window.innerHeight
41+
42+
// Fade out previous trails for smoother effect
43+
ctx.fillStyle = "rgba(0, 0, 0, 0.05)" // Adjust opacity for smooth fading
44+
ctx.fillRect(0, 0, canvas.width, canvas.height)
45+
46+
// Add blur for softer trail effect
47+
ctx.filter = "blur(1px)"
48+
49+
const positions = calculateTrailPositions(
50+
prevPositionRef.current,
51+
positionRef.current,
52+
8, // Increased segments for smoother trail
53+
)
54+
55+
positions.forEach((pos, index) => {
56+
const decayFactor = easeOut(1 - index / positions.length)
57+
const trailSize = Math.max(size * decayFactor, 1) // Ensures it doesn't go to 0
58+
59+
ctx.beginPath()
60+
ctx.arc(pos.x, pos.y, trailSize / 2, 0, Math.PI * 2)
61+
ctx.fillStyle = `rgba(255, 255, 255, ${decayFactor * 0.8})`
62+
ctx.fill()
63+
64+
ctx.strokeStyle = theme === "dark" ? "rgba(255, 255, 255, 0.8)" : "rgba(0, 0, 0, 0.8)"
65+
ctx.lineWidth = 1.5
66+
ctx.stroke()
67+
})
68+
69+
// Reset filter after drawing
70+
ctx.filter = "none"
71+
72+
animationFrameRef.current = requestAnimationFrame(drawTrail)
73+
}
74+
75+
const calculateTrailPositions = (
76+
prev: { x: number; y: number },
77+
current: { x: number; y: number },
78+
segments: number,
79+
) => {
80+
const positions = []
81+
for (let i = 0; i < segments; i++) {
82+
const ratio = i / segments
83+
positions.push({
84+
x: current.x * (1 - ratio) + prev.x * ratio,
85+
y: current.y * (1 - ratio) + prev.y * ratio,
86+
})
87+
}
88+
return positions
89+
}
90+
91+
const easeOut = (t: number) => 1 - Math.pow(1 - t, 3) // Smooth fade-out effect
92+
93+
document.addEventListener("mousemove", handleMouseMove)
94+
animationFrameRef.current = requestAnimationFrame(drawTrail)
95+
96+
const handleResize = () => {
97+
if (trailRef.current) {
98+
trailRef.current.width = window.innerWidth
99+
trailRef.current.height = window.innerHeight
100+
}
101+
}
102+
103+
window.addEventListener("resize", handleResize)
104+
handleResize()
105+
106+
return () => {
107+
document.removeEventListener("mousemove", handleMouseMove)
108+
window.removeEventListener("resize", handleResize)
109+
if (animationFrameRef.current) {
110+
cancelAnimationFrame(animationFrameRef.current)
111+
}
112+
}
113+
}, [isActive, size, theme])
114+
115+
if (!isActive) return null
116+
117+
return (
118+
<>
119+
<canvas ref={trailRef} className="fixed inset-0 pointer-events-none z-50" />
120+
<div
121+
ref={cursorRef}
122+
className="fixed top-0 left-0 pointer-events-none z-50"
123+
style={{
124+
width: `${size}px`,
125+
height: `${size}px`,
126+
borderRadius: "50%",
127+
backgroundColor: "white",
128+
border: `1.5px solid ${theme === "dark" ? "white" : "black"}`,
129+
transform: "translate(-50%, -50%)",
130+
boxShadow: "0 0 5px rgba(0, 0, 0, 0.2)",
131+
}}
132+
/>
133+
</>
134+
)
135+
}
136+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.cursorHidden {
2+
cursor: none !important;
3+
}
4+
5+
.trailCanvas {
6+
position: fixed;
7+
top: 0;
8+
left: 0;
9+
width: 100%;
10+
height: 100%;
11+
pointer-events: none;
12+
z-index: 50;
13+
}
14+
15+
.cursorDot {
16+
position: fixed;
17+
pointer-events: none;
18+
z-index: 51;
19+
transform: translate(-50%, -50%);
20+
border-radius: 50%;
21+
background-color: white;
22+
transition: width 0.2s ease, height 0.2s ease;
23+
}

0 commit comments

Comments
 (0)