{"version":3,"file":"TrailingCursor.CHInZuRF.mjs","names":["useState","useRef","newTrail"],"sources":["https:/framerusercontent.com/modules/VbR6ciSVPVGUvKjRWVGP/cnYyCBtUpF4ZgHoMTJ76/TrailingCursor.js"],"sourcesContent":["// Custom Cursor with movement-based trail\nimport{jsx as _jsx,jsxs as _jsxs}from\"react/jsx-runtime\";import{useEffect,useRef,useState}from\"react\";import{addPropertyControls,ControlType,RenderTarget,useIsStaticRenderer}from\"framer\";/**\n * Custom Cursor with Trailing Squares\n *\n * Shows a square cursor that follows the pointer and renders a fading trail of squares. Trail length and opacity are based on recent pointer movement intensity. Color is configurable.\n *\n * @framerSupportedLayoutWidth fixed\n * @framerSupportedLayoutHeight fixed\n */export default function TrailingCursor(props){const{color,size,trailBaseLength,trailMaxLength,fade,style,speed=1}=props;const isStatic=useIsStaticRenderer();// Don't show on thumbnail/canvas/static\nif(isStatic||RenderTarget.current()!==RenderTarget.preview)return null;// Trailing functionality\nconst[trail,setTrail]=useState([]);const[visible,setVisible]=useState(false);const trailRef=useRef([]);const lastPos=useRef(null);const lastMoveTime=useRef(0);const isIdleRef=useRef(true);// Decay controls\n// const DECAY_POINTS_PER_FRAME = 3\n// Aggressiveness and easing for visually natural decay\n// ---- Eased decay replaced with time-consistent frame-based closing\nconst CLOSE_DURATION=300// ms to fully close any trail\n;const IDLE_THRESHOLD=90// ms\n;const MIN_MOVE_DISTANCE=1.8// px minimum move distance required to add new point\n;// Called in RAF — on idle, determines pointsPerFrame for ~CLOSE_DURATION fade\nfunction getDecayRate(trailLen,frameTime){if(trailLen<=1)return 1;// New: time-consistent, frame-based decay rate so closing duration is ~CLOSE_DURATION\nconst frames=Math.max(1,CLOSE_DURATION/frameTime);let pointsPerFrame=trailLen/frames;// Clamp: min 1, max 3 points per frame\npointsPerFrame=Math.max(1,Math.min(3,pointsPerFrame));return pointsPerFrame;}const nowTime=()=>typeof performance!==\"undefined\"&&typeof performance.now===\"function\"?performance.now():Date.now();// Handle mouse movement & add points\nuseEffect(()=>{function handleMove(e){const x=e.clientX;const y=e.clientY;const now=nowTime();// Detect if cursor is inside document bounds; if not, ignore movement\nif(typeof document!==\"undefined\"){const inDoc=x>=0&&x<=window.innerWidth&&y>=0&&y<=window.innerHeight;if(!inDoc){// If cursor leaves doc, ignore event\nreturn;}}if(lastPos.current){const dx=x-lastPos.current.x;const dy=y-lastPos.current.y;const dist=Math.sqrt(dx*dx+dy*dy);// Ignore micro-movements completely: no trail mutation, no idle reset\nif(dist<MIN_MOVE_DISTANCE){return;}// While idle, only exit idle on meaningful movement\nif(isIdleRef.current){// Only reset idle state when movement exceeds threshold\nisIdleRef.current=false;}// If still idle after check, ignore this event entirely\nif(isIdleRef.current){return;}// Append new point at head only when movement is meaningful\nconst maxLen=props.trailMaxLength||200;const appended=[...trailRef.current,{x,y}];trailRef.current=appended.length>maxLen?appended.slice(appended.length-maxLen):appended;lastPos.current={x,y};lastMoveTime.current=now;setTrail(trailRef.current.slice());setVisible(true);return;}// First meaningful movement initializes the trail and clears idle\nlastPos.current={x,y};trailRef.current=[{x,y}];lastMoveTime.current=now;isIdleRef.current=false;setTrail([{x,y}]);setVisible(true);}if(typeof window!==\"undefined\"){window.addEventListener(\"mousemove\",handleMove);}return()=>{if(typeof window!==\"undefined\"){window.removeEventListener(\"mousemove\",handleMove);}};},[]);// Main RAF loop for deterministic decay & idle logic\nconst lastDecayTime=useRef(null);const rafIdRef=useRef(null);// Single persistent RAF loop; we don't toggle this on/off based on trail state\n// --- Add ref to hold _pendingHide state (was incorrectly assigned on function)\nconst pendingHideRef=useRef(0);// --- Add refs to hold idleDecay and headLinger values (fix type error)\nconst idleDecayStartLenRef=useRef(null);const idleDecayStartTimeRef=useRef(null);const headLingerStartRef=useRef(null);// PHASE 1: Soft decay (removes one point per frame, keeps head anchored until idle)\n// PHASE 2: Idle decay with smooth closing\nfunction fadeTrail(now){const t=now??nowTime();if(lastDecayTime.current==null){lastDecayTime.current=t;}const dt=t-lastDecayTime.current;const lastMove=lastMoveTime.current;const idle=t-lastMove>IDLE_THRESHOLD;const currentTrail=trailRef.current;if(idle){isIdleRef.current=true;}// If idle and trail is empty, just ensure state is cleared; RAF stays alive\nif(idle&&currentTrail.length===0){setTrail([]);if(!pendingHideRef.current){pendingHideRef.current=1;}else if(pendingHideRef.current===1){setVisible(false);pendingHideRef.current=0;}lastDecayTime.current=t;return;}else{pendingHideRef.current=0;}// Nothing to decay, but keep React state in sync\nif(currentTrail.length===0){lastDecayTime.current=t;setTrail([]);return;}const len=currentTrail.length;// --- PHASE 1: immediate soft decay if moving (no pause)\nif(!idle&&len>(props.minTrailLength||2)){// Remove exactly one from tail (never touch head)\nconst newTrail=currentTrail.slice(1);trailRef.current=newTrail;setTrail(newTrail.slice());lastDecayTime.current=t;// No phase 2 triggered (do NOT update idle decay refs)\nidleDecayStartLenRef.current=null;idleDecayStartTimeRef.current=null;headLingerStartRef.current=null;return;}// --- PHASE 2: idle decay with smooth closing\n// Easing for decay:\nif(!idleDecayStartLenRef.current&&idle){idleDecayStartLenRef.current=len;idleDecayStartTimeRef.current=t;}if(!idle){idleDecayStartLenRef.current=null;idleDecayStartTimeRef.current=null;}// --- REWRITE: Use time-consistent, frame-based decay rate (no per-frame clamps, no multi-rate logic)\nlet removeCount=0;if(idle){const CLOSE_DURATION=310// ms\n;const headLinger=70// ms\n;const startLen=idleDecayStartLenRef.current||len;const startT=idleDecayStartTimeRef.current||t;const timeSinceIdle=Math.max(0,t-startT);const frameTime=Math.max(16,dt);// Compute points per frame for time-consistent closing\nconst frames=Math.max(1,CLOSE_DURATION/frameTime);let pointsPerFrame=startLen/frames;pointsPerFrame=Math.max(1,pointsPerFrame);// Use Math.ceil to avoid rounding jitter\nremoveCount=Math.ceil(pointsPerFrame);// Never remove more than len-1 from tail\nremoveCount=Math.max(0,Math.min(removeCount,len-1));}else{removeCount=0;}let newTrail=currentTrail;if(idle){if(len===1){// Head lingers for a short time after tail disappears\nif(!headLingerStartRef.current){headLingerStartRef.current=t;}if(t-headLingerStartRef.current<70){newTrail=currentTrail;}else{newTrail=[];headLingerStartRef.current=null;}}else if(removeCount>0){newTrail=currentTrail.slice(removeCount);headLingerStartRef.current=null;}}trailRef.current=newTrail;lastDecayTime.current=t;setTrail(newTrail.slice());}useEffect(()=>{function loop(time){fadeTrail(time);// Always continue the single RAF loop while component is mounted\nrafIdRef.current=requestAnimationFrame(loop);}// Start the persistent loop once on mount\nrafIdRef.current=requestAnimationFrame(loop);return()=>{if(rafIdRef.current!==null){cancelAnimationFrame(rafIdRef.current);rafIdRef.current=null;}};},[]);// Hide default cursor\nuseEffect(()=>{return()=>{if(typeof document!==\"undefined\"&&document.body){document.body.style.cursor=\"\";}};},[]);if(!visible)return null;return /*#__PURE__*/_jsxs(\"div\",{style:{pointerEvents:\"none\",position:\"fixed\",inset:0,zIndex:999999,...style},children:[trail.length>1&&/*#__PURE__*/_jsx(\"svg\",{width:\"100vw\",height:\"100vh\",style:{position:\"fixed\",left:0,top:0,pointerEvents:\"none\",zIndex:999999,width:\"100vw\",height:\"100vh\",overflow:\"visible\",shapeRendering:\"geometricPrecision\"},children:/*#__PURE__*/_jsx(\"path\",{d:getSmoothTrailPath(trail),fill:\"none\",stroke:color,strokeWidth:size,strokeLinecap:\"round\",strokeLinejoin:\"round\",vectorEffect:\"non-scaling-stroke\",style:{shapeRendering:\"geometricPrecision\",vectorEffect:\"non-scaling-stroke\"}})}),trail.length>0&&/*#__PURE__*/_jsx(\"div\",{style:{position:\"fixed\",left:trail[trail.length-1].x-size/2,top:trail[trail.length-1].y-size/2,width:size,height:size,borderRadius:0,background:color,opacity:1,boxShadow:\"0 1px 10px 0 rgba(0,0,0,0.10)\",transition:\"none\",willChange:\"transform, opacity\",pointerEvents:\"none\"}})]});}// --- utility for trail smoothing ---\nfunction getSmoothTrailPath(trail){if(trail.length===0)return\"\";if(trail.length===1){const{x,y}=trail[0];return`M${x},${y}`;}// Midpoint smoothing\nconst pts=trail;let d=\"M\"+pts[0].x+\",\"+pts[0].y;for(let i=0;i<pts.length-1;i++){const p0=pts[i];const p1=pts[i+1];// Use the midpoint between p0 and p1 as the curve's control point and destination\nconst mx=(p0.x+p1.x)/2;const my=(p0.y+p1.y)/2;if(i===0){d+=` Q${p0.x},${p0.y} ${mx},${my}`;}else{d+=` Q${p0.x},${p0.y} ${mx},${my}`;}}// To the last point\nd+=` T${pts[pts.length-1].x},${pts[pts.length-1].y}`;return d;}addPropertyControls(TrailingCursor,{color:{type:ControlType.Color,title:\"Color\",defaultValue:\"#0099FF\"},size:{type:ControlType.Number,title:\"Size\",defaultValue:4,min:2,max:60,unit:\"px\"},trailBaseLength:{type:ControlType.Number,title:\"Trail Min\",defaultValue:4,min:1,max:14,step:1},// MINIMUM TRAIL LENGTH PROPERTY\nminTrailLength:{type:ControlType.Number,title:\"Min Trail Length\",defaultValue:2,min:1,max:14,step:1},trailMaxLength:{type:ControlType.Number,title:\"Trail Max\",defaultValue:14,min:4,max:200,step:1},fade:{type:ControlType.Number,title:\"Trail Fade\",defaultValue:.17,min:.05,max:.6,step:.01,hidden:true},delay:{type:ControlType.Number,title:\"Trail Delay\",defaultValue:10,min:5,max:1e3,step:5},speed:{type:ControlType.Number,title:\"Trail Speed\",defaultValue:1,min:.3,max:4,step:.05,unit:\"x\"}});\nexport const __FramerMetadata__ = {\"exports\":{\"default\":{\"type\":\"reactComponent\",\"name\":\"TrailingCursor\",\"slots\":[],\"annotations\":{\"framerContractVersion\":\"1\",\"framerSupportedLayoutWidth\":\"fixed\",\"framerSupportedLayoutHeight\":\"fixed\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./TrailingCursor.map"],"mappings":"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"}