56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
'use client';
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { useRecordMode } from './RecordModeContext';
|
|
|
|
export function PlaybackCursor() {
|
|
const { isPlaying, cursorPosition } = useRecordMode();
|
|
const [scrollOffset, setScrollOffset] = useState({ x: 0, y: 0 });
|
|
|
|
// Track scroll so cursor stays locked to the correct element
|
|
useEffect(() => {
|
|
if (!isPlaying) return;
|
|
|
|
const handleScroll = () => {
|
|
setScrollOffset({ x: window.scrollX, y: window.scrollY });
|
|
};
|
|
|
|
handleScroll(); // Init
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, [isPlaying]);
|
|
|
|
if (!isPlaying) return null;
|
|
|
|
return (
|
|
<motion.div
|
|
className="fixed z-[10000] pointer-events-none"
|
|
animate={{
|
|
x: cursorPosition.x,
|
|
y: cursorPosition.y,
|
|
}}
|
|
transition={{
|
|
type: 'spring',
|
|
damping: 30,
|
|
stiffness: 250,
|
|
mass: 0.5,
|
|
}}
|
|
>
|
|
{/* Outer Pulse Ring */}
|
|
<div className="absolute -inset-3 rounded-full bg-[#82ed20]/10 animate-ping" />
|
|
|
|
{/* Visual Cursor */}
|
|
<div className="relative">
|
|
{/* Soft Glow */}
|
|
<div className="absolute -inset-2 bg-[#82ed20]/20 rounded-full blur-md" />
|
|
|
|
{/* Pointer Arrow */}
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="drop-shadow-[0_2px_8px_rgba(130,237,32,0.5)]">
|
|
<path d="M3 3L10.07 19.97L12.58 12.58L19.97 10.07L3 3Z" fill="white" stroke="black" strokeWidth="1.5" strokeLinejoin="round" />
|
|
</svg>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
}
|