Files
klz-cables.com/components/record-mode/PlaybackCursor.tsx
Marc Mintel 16d06d3275
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🧪 QA (push) Successful in 2m1s
Build & Deploy / 🏗️ Build (push) Successful in 7m43s
Build & Deploy / 🚀 Deploy (push) Successful in 26s
Build & Deploy / 🧪 Smoke Test (push) Successful in 1m10s
Build & Deploy / ⚡ Lighthouse (push) Successful in 3m20s
Build & Deploy / 🔔 Notify (push) Successful in 2s
perf: deep react code splitting, next-intl payload scoping, and SVG hardware acceleration for PageSpeed 100
2026-02-20 11:53:42 +01:00

93 lines
3.1 KiB
TypeScript

'use client';
import React, { useEffect, useState } from 'react';
import { m, LazyMotion, domAnimation, AnimatePresence } from 'framer-motion';
import { useRecordMode } from './RecordModeContext';
export function PlaybackCursor() {
const { isPlaying, cursorPosition, isClicking } = 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 (
<LazyMotion strict features={domAnimation}>
<m.div
className="fixed z-[10000] pointer-events-none"
animate={{
x: cursorPosition.x,
y: cursorPosition.y,
scale: isClicking ? 0.8 : 1,
rotateX: isClicking ? 15 : 0,
rotateY: isClicking ? -15 : 0,
}}
transition={{
x: { type: 'spring', damping: 30, stiffness: 250, mass: 0.5 },
y: { type: 'spring', damping: 30, stiffness: 250, mass: 0.5 },
scale: { type: 'spring', damping: 15, stiffness: 400 },
rotateX: { type: 'spring', damping: 15, stiffness: 400 },
rotateY: { type: 'spring', damping: 15, stiffness: 400 },
}}
style={{ perspective: '1000px' }}
>
<AnimatePresence>
{isClicking && (
<m.div
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 2.5, opacity: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.4, ease: 'easeOut' }}
className="absolute inset-0 rounded-full border-2 border-[#82ed20] blur-[1px]"
/>
)}
</AnimatePresence>
{/* Outer Pulse Ring */}
<div
className={`absolute -inset-3 rounded-full bg-[#82ed20]/10 ${isClicking ? 'scale-150 opacity-0' : 'animate-ping'} transition-all duration-300`}
/>
{/* Visual Cursor */}
<div className="relative">
{/* Soft Glow */}
<div
className={`absolute -inset-2 bg-[#82ed20]/20 rounded-full blur-md transition-all ${isClicking ? 'bg-[#82ed20]/50 blur-xl' : ''}`}
/>
{/* 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)] transition-transform ${isClicking ? 'translate-y-0.5' : ''}`}
>
<path
d="M3 3L10.07 19.97L12.58 12.58L19.97 10.07L3 3Z"
fill={isClicking ? '#82ed20' : 'white'}
stroke="black"
strokeWidth="1.5"
strokeLinejoin="round"
className="transition-colors duration-150"
/>
</svg>
</div>
</m.div>
</LazyMotion>
);
}