({
className,
style,
trailLength = 50,
inertia = 0.5,
grainIntensity = 0.05,
bloomStrength = 0.1,
bloomRadius = 1.0,
bloomThreshold = 0.025,
brightness = 1,
color = '#B497CF',
mixBlendMode = 'screen',
edgeIntensity = 0,
maxDevicePixelRatio = 0.5,
targetPixels,
fadeDelayMs,
fadeDurationMs,
zIndex = 10
})
| 29 | }; |
| 30 | |
| 31 | const GhostCursor: React.FC<GhostCursorProps> = ({ |
| 32 | className, |
| 33 | style, |
| 34 | trailLength = 50, |
| 35 | inertia = 0.5, |
| 36 | grainIntensity = 0.05, |
| 37 | bloomStrength = 0.1, |
| 38 | bloomRadius = 1.0, |
| 39 | bloomThreshold = 0.025, |
| 40 | |
| 41 | brightness = 1, |
| 42 | color = '#B497CF', |
| 43 | mixBlendMode = 'screen', |
| 44 | edgeIntensity = 0, |
| 45 | |
| 46 | maxDevicePixelRatio = 0.5, |
| 47 | targetPixels, |
| 48 | |
| 49 | fadeDelayMs, |
| 50 | fadeDurationMs, |
| 51 | zIndex = 10 |
| 52 | }) => { |
| 53 | const containerRef = useRef<HTMLDivElement | null>(null); |
| 54 | const rendererRef = useRef<THREE.WebGLRenderer | null>(null); |
| 55 | const composerRef = useRef<EffectComposer | null>(null); |
| 56 | const materialRef = useRef<THREE.ShaderMaterial | null>(null); |
| 57 | const bloomPassRef = useRef<UnrealBloomPass | null>(null); |
| 58 | const filmPassRef = useRef<ShaderPass | null>(null); |
| 59 | |
| 60 | // Trail circular buffer |
| 61 | const trailBufRef = useRef<THREE.Vector2[]>([]); |
| 62 | const headRef = useRef(0); |
| 63 | |
| 64 | const rafRef = useRef<number | null>(null); |
| 65 | const resizeObsRef = useRef<ResizeObserver | null>(null); |
| 66 | const currentMouseRef = useRef(new THREE.Vector2(0.5, 0.5)); |
| 67 | const velocityRef = useRef(new THREE.Vector2(0, 0)); |
| 68 | const fadeOpacityRef = useRef(1.0); |
| 69 | const lastMoveTimeRef = useRef(typeof performance !== 'undefined' ? performance.now() : Date.now()); |
| 70 | const pointerActiveRef = useRef(false); |
| 71 | const runningRef = useRef(false); |
| 72 | const hasValidSizeRef = useRef(false); |
| 73 | |
| 74 | const isTouch = useMemo( |
| 75 | () => typeof window !== 'undefined' && ('ontouchstart' in window || navigator.maxTouchPoints > 0), |
| 76 | [] |
| 77 | ); |
| 78 | |
| 79 | const pixelBudget = targetPixels ?? (isTouch ? 0.9e6 : 1.3e6); |
| 80 | const fadeDelay = fadeDelayMs ?? (isTouch ? 500 : 1000); |
| 81 | const fadeDuration = fadeDurationMs ?? (isTouch ? 1000 : 1500); |
| 82 | |
| 83 | const baseVertexShader = ` |
| 84 | varying vec2 vUv; |
| 85 | void main() { |
| 86 | vUv = uv; |
| 87 | gl_Position = vec4(position, 1.0); |
| 88 | } |
nothing calls this directly
no test coverage detected