{"version":3,"file":"TextPath.CUqImN31.mjs","names":["useRef","useMemo","useCallback","offsetPercent"],"sources":["https:/framerusercontent.com/modules/qIknDiyRLMYEVQRW0vw1/nK7aiNbDVWAOv2rxmFQ1/TextPath.js"],"sourcesContent":["// Text Path Component - Animates text along an SVG path with customizable properties\nimport{jsx as _jsx,jsxs as _jsxs}from\"react/jsx-runtime\";import{useEffect,useRef,useMemo,useCallback,useLayoutEffect}from\"react\";import{addPropertyControls,ControlType,RenderTarget}from\"framer\";import{useInView}from\"framer-motion\";// Helper function to calculate viewBox from SVG path data\nfunction calculateViewBoxFromPath(pathData,strokeWidth){try{// Parse path commands to find min/max coordinates\nconst commands=pathData.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi)||[];let minX=Infinity;let minY=Infinity;let maxX=-Infinity;let maxY=-Infinity;commands.forEach(cmd=>{const type=cmd[0].toUpperCase();const coords=cmd.slice(1).trim().split(/[\\s,]+/).map(Number).filter(n=>!isNaN(n));// Extract x,y coordinates based on command type\nfor(let i=0;i<coords.length;i+=2){if(coords[i]!==undefined){minX=Math.min(minX,coords[i]);maxX=Math.max(maxX,coords[i]);}if(coords[i+1]!==undefined){minY=Math.min(minY,coords[i+1]);maxY=Math.max(maxY,coords[i+1]);}}});// Add padding for stroke\nconst padding=strokeWidth/2+5;const width=maxX-minX+padding*2;const height=maxY-minY+padding*2;return`${minX-padding} ${minY-padding} ${width} ${height}`;}catch(e){// Fallback to default\nreturn\"0 0 3192 367\";}}/**\n * Text Follow Path\n *\n * @framerIntrinsicWidth 600\n * @framerIntrinsicHeight 400\n *\n * @framerSupportedLayoutWidth fixed\n * @framerSupportedLayoutHeight fixed\n * @framerDisableUnlink\n */export default function TextFollowPath(props){const{text=\"Text follows the path\",svgPath=\"M12.8009 210.181C12.8009 210.181 111.532 372.298 371.301 349.681C705.301 320.6 906.801 6.81913 1201.3 31.9999C1463.38 54.4084 1490.3 349.681 1838.8 349.681C2283.8 349.681 2356.8 15 2677.8 15C2934.6 15 3118.13 238.12 3177.8 349.681\",textColor=\"#000000\",pathColor=\"#FFA8F2\",showPath=true,direction=\"forward\",speed=50,font,pathSize=30,preview=true,style}=props;const containerRef=useRef(null);const svgRef=useRef(null);const pathRef=useRef(null);const textRef=useRef(null);const textPathRef=useRef(null);const offsetRef=useRef(0);const animationRef=useRef();const lastAppliedOffsetRef=useRef(0);// Use refs instead of state for measurements\nconst pathLengthRef=useRef(3192);const textWidthRef=useRef(100);const hasMeasured=useRef(false);const isInViewRef=useRef(false);// Track viewport visibility\nconst isInView=useInView(containerRef,{once:false,amount:.1});// Update ref when visibility changes\nuseEffect(()=>{isInViewRef.current=isInView;},[isInView]);// Extract path d attribute if full SVG is pasted\nconst processedPath=useMemo(()=>{// Check if the input contains SVG tags\nif(svgPath.includes(\"<path\")&&svgPath.includes(\"d=\")){// Extract the d attribute value\nconst dMatch=svgPath.match(/d=\"([^\"]+)\"/);if(dMatch&&dMatch[1]){return dMatch[1];}}// If it's already just a path or no match found, use as is\nreturn svgPath;},[svgPath]);// Calculate viewBox synchronously from path data\nconst viewBox=useMemo(()=>{return calculateViewBoxFromPath(processedPath,pathSize);},[processedPath,pathSize]);const width=style?.width===\"100%\"?\"100%\":600;const height=style?.height===\"100%\"?\"100%\":400;// Calculate repetitions and repeated text from refs\nconst repetitions=useMemo(()=>{const reps=Math.max(3,Math.ceil(pathLengthRef.current*2/textWidthRef.current));return Math.min(reps,50)// Cap at 50 to prevent excessive memory usage\n;},[text,font,processedPath]);const repeatedText=useMemo(()=>{return Array(repetitions).fill(text).join(\" \");},[repetitions,text]);// Measure text width and path length - optimized to run only when necessary\nconst measureElements=useCallback(()=>{if(textRef.current&&pathRef.current){const textElement=textRef.current;const pathElement=pathRef.current;try{const measuredTextWidth=textElement.getComputedTextLength()/repetitions;const measuredPathLength=pathElement.getTotalLength();if(measuredTextWidth>0&&measuredPathLength>0){// Write directly to refs - no state updates\ntextWidthRef.current=measuredTextWidth;pathLengthRef.current=measuredPathLength;}hasMeasured.current=true;}catch(e){// Fallback to defaults\n}}},[repetitions]);useEffect(()=>{// Reset measurement flag when key dependencies change\nhasMeasured.current=false;},[text,font,processedPath]);useLayoutEffect(()=>{if(!hasMeasured.current){measureElements();}},[measureElements]);// Animation loop - optimized to update DOM directly\nuseEffect(()=>{// Don't animate on canvas when preview is false\nconst isCanvas=RenderTarget.current()===RenderTarget.canvas;if(isCanvas&&!preview){return;}// Don't animate if speed is 0\nif(speed===0){return;}let lastTime=performance.now();const directionMultiplier=direction===\"forward\"?1:-1;const pixelsPerSecond=speed*2;const UPDATE_THRESHOLD=.5// Only update DOM if offset changes by at least 0.5 pixels\n;const animate=currentTime=>{// Check ref instead of dependency - no effect restart needed\nif(!isInViewRef.current){animationRef.current=requestAnimationFrame(animate);return;}const deltaTime=(currentTime-lastTime)/1e3// Convert to seconds\n;lastTime=currentTime;const increment=pixelsPerSecond*deltaTime*directionMultiplier;offsetRef.current+=increment;const pathLength=pathLengthRef.current;// Loop seamlessly using path length\nif(offsetRef.current>=pathLength){offsetRef.current=offsetRef.current%pathLength;lastAppliedOffsetRef.current=offsetRef.current;}else if(offsetRef.current<0){offsetRef.current=pathLength+offsetRef.current%pathLength;lastAppliedOffsetRef.current=offsetRef.current;}// Only update DOM if the change is significant enough to be visible\nconst offsetDelta=Math.abs(offsetRef.current-lastAppliedOffsetRef.current);if(offsetDelta>=UPDATE_THRESHOLD){// Update DOM directly for better performance\nif(textPathRef.current){const offsetPercent=offsetRef.current/pathLength*100;textPathRef.current.setAttribute(\"startOffset\",`${-offsetPercent}%`);lastAppliedOffsetRef.current=offsetRef.current;}}animationRef.current=requestAnimationFrame(animate);};if(typeof window!==\"undefined\"){animationRef.current=requestAnimationFrame(animate);}return()=>{if(animationRef.current){cancelAnimationFrame(animationRef.current);}};},[speed,direction,preview]);// Initial offset for SSR\nconst offsetPercent=offsetRef.current/pathLengthRef.current*100;// Determine if animation should be active\nconst shouldAnimate=useMemo(()=>{const isCanvas=RenderTarget.current()===RenderTarget.canvas;return isInView&&speed>0&&(preview||!isCanvas);},[isInView,speed,preview]);return /*#__PURE__*/_jsx(\"div\",{ref:containerRef,style:{...style,width:\"100%\",height:\"100%\",position:\"relative\",overflow:\"hidden\"},children:/*#__PURE__*/_jsxs(\"svg\",{ref:svgRef,width:\"100%\",height:\"100%\",viewBox:viewBox,preserveAspectRatio:\"xMidYMid meet\",style:{display:\"block\"},children:[/*#__PURE__*/_jsx(\"defs\",{children:/*#__PURE__*/_jsx(\"path\",{ref:pathRef,id:\"textPath\",d:processedPath})}),showPath&&/*#__PURE__*/_jsx(\"path\",{d:processedPath,fill:\"none\",stroke:pathColor,strokeWidth:pathSize,strokeLinecap:\"round\",strokeLinejoin:\"round\"}),/*#__PURE__*/_jsx(\"text\",{ref:textRef,fill:textColor,style:{...font},children:/*#__PURE__*/_jsx(\"textPath\",{ref:textPathRef,href:\"#textPath\",startOffset:`${-offsetPercent}%`,textAnchor:\"start\",dominantBaseline:\"middle\",style:{fontSize:font.fontSize||\"32px\",fontFamily:font.fontFamily,fontWeight:font.fontWeight,fontStyle:font.fontStyle,letterSpacing:font.letterSpacing,willChange:shouldAnimate?\"auto\":\"auto\"},children:repeatedText})})]})});}addPropertyControls(TextFollowPath,{text:{type:ControlType.String,title:\"Text\",defaultValue:\"Text follows the path\",displayTextArea:true},svgPath:{type:ControlType.String,title:\"SVG Path\",defaultValue:\"M12.8009 210.181C12.8009 210.181 111.532 372.298 371.301 349.681C705.301 320.6 906.801 6.81913 1201.3 31.9999C1463.38 54.4084 1490.3 349.681 1838.8 349.681C2283.8 349.681 2356.8 15 2677.8 15C2934.6 15 3118.13 238.12 3177.8 349.681\",displayTextArea:true},font:{type:ControlType.Font,title:\"Font\",controls:\"extended\",defaultFontType:\"sans-serif\",defaultValue:{fontSize:\"32px\",variant:\"Semibold\",letterSpacing:\"-0.03em\",lineHeight:\"1em\"}},textColor:{type:ControlType.Color,title:\"Text Color\",defaultValue:\"#000000\"},showPath:{type:ControlType.Boolean,title:\"Show Path\",defaultValue:true,enabledTitle:\"Show\",disabledTitle:\"Hide\"},pathSize:{type:ControlType.Number,title:\"Path Size\",defaultValue:30,min:1,max:100,step:1},pathColor:{type:ControlType.Color,title:\"Path Color\",defaultValue:\"#FFA8F2\"},direction:{type:ControlType.Enum,title:\"Direction\",options:[\"forward\",\"backward\"],optionTitles:[\"←\",\"→\"],defaultValue:\"forward\",displaySegmentedControl:true},speed:{type:ControlType.Number,title:\"Speed\",defaultValue:50,min:0,max:200,step:1},preview:{type:ControlType.Boolean,title:\"Preview\",defaultValue:true,enabledTitle:\"Yes\",disabledTitle:\"No\",description:\"More resources at [Velox Themes](https://veloxthemes.com)\"}});\nexport const __FramerMetadata__ = {\"exports\":{\"default\":{\"type\":\"reactComponent\",\"name\":\"TextFollowPath\",\"slots\":[],\"annotations\":{\"framerContractVersion\":\"1\",\"framerIntrinsicWidth\":\"600\",\"framerDisableUnlink\":\"\",\"framerIntrinsicHeight\":\"400\",\"framerSupportedLayoutHeight\":\"fixed\",\"framerSupportedLayoutWidth\":\"fixed\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./TextPath.map"],"mappings":"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"}