{"version":3,"file":"follow-value.mjs","sources":["../../../src/value/follow-value.ts"],"sourcesContent":["import { MotionValue, motionValue } from \".\"\nimport { JSAnimation } from \"../animation/JSAnimation\"\nimport { AnyResolvedKeyframe, ValueAnimationTransition } from \"../animation/types\"\nimport { frame } from \"../frameloop\"\nimport { isMotionValue } from \"./utils/is-motion-value\"\n\n/**\n * Options for useFollowValue hook, extending ValueAnimationTransition\n * but excluding lifecycle callbacks that don't make sense for the hook pattern.\n */\nexport type FollowValueOptions = Omit<\n ValueAnimationTransition,\n \"onUpdate\" | \"onComplete\" | \"onPlay\" | \"onRepeat\" | \"onStop\"\n> & {\n /**\n * When true, the first change from a tracked `MotionValue` source\n * will jump to the new value instead of animating. Subsequent\n * changes animate normally. This prevents unwanted animations\n * on page refresh or back navigation (e.g. `useScroll` + `useSpring`).\n *\n * @default false\n */\n skipInitialAnimation?: boolean\n}\n\n/**\n * Create a `MotionValue` that animates to its latest value using any transition type.\n * Can either be a value or track another `MotionValue`.\n *\n * ```jsx\n * const x = motionValue(0)\n * const y = followValue(x, { type: \"spring\", stiffness: 300 })\n * // or with tween\n * const z = followValue(x, { type: \"tween\", duration: 0.5, ease: \"easeOut\" })\n * ```\n *\n * @param source - Initial value or MotionValue to track\n * @param options - Animation transition options\n * @returns `MotionValue`\n *\n * @public\n */\nexport function followValue(\n source: T | MotionValue,\n options?: FollowValueOptions\n) {\n const initialValue = isMotionValue(source) ? source.get() : source\n const value = motionValue(initialValue)\n\n attachFollow(value, source, options)\n\n return value\n}\n\n/**\n * Attach an animation to a MotionValue that will animate whenever the value changes.\n * Similar to attachSpring but supports any transition type (spring, tween, inertia, etc.)\n *\n * @param value - The MotionValue to animate\n * @param source - Initial value or MotionValue to track\n * @param options - Animation transition options\n * @returns Cleanup function\n *\n * @public\n */\nexport function attachFollow(\n value: MotionValue,\n source: T | MotionValue,\n options: FollowValueOptions = {}\n): VoidFunction {\n const initialValue = value.get()\n\n let activeAnimation: JSAnimation | null = null\n let latestValue = initialValue\n let latestSetter: (v: T) => void\n\n const unit =\n typeof initialValue === \"string\"\n ? initialValue.replace(/[\\d.-]/g, \"\")\n : undefined\n\n const stopAnimation = () => {\n if (activeAnimation) {\n activeAnimation.stop()\n activeAnimation = null\n }\n }\n\n const startAnimation = () => {\n const currentValue = asNumber(value.get())\n const targetValue = asNumber(latestValue)\n\n // Don't animate if we're already at the target\n if (currentValue === targetValue) {\n stopAnimation()\n return\n }\n\n // Use the running animation's analytical velocity for accuracy,\n // falling back to the MotionValue's velocity for the initial animation.\n // This prevents systematic velocity loss at high frame rates (240hz+).\n const velocity = activeAnimation\n ? activeAnimation.getGeneratorVelocity()\n : value.getVelocity()\n\n stopAnimation()\n\n activeAnimation = new JSAnimation({\n keyframes: [currentValue, targetValue],\n velocity,\n // Default to spring if no type specified (matches useSpring behavior)\n type: \"spring\",\n restDelta: 0.001,\n restSpeed: 0.01,\n ...options,\n onUpdate: latestSetter,\n })\n }\n\n // Use a stable function reference so the frame loop Set deduplicates\n // multiple calls within the same frame (e.g. rapid mouse events)\n const scheduleAnimation = () => {\n startAnimation()\n value[\"events\"].animationStart?.notify()\n activeAnimation?.then(() => {\n value[\"events\"].animationComplete?.notify()\n })\n }\n\n value.attach((v, set) => {\n latestValue = v\n latestSetter = (latest) => set(parseValue(latest, unit) as T)\n frame.postRender(scheduleAnimation)\n }, stopAnimation)\n\n if (isMotionValue(source)) {\n let skipNextAnimation = options.skipInitialAnimation === true\n\n const removeSourceOnChange = source.on(\"change\", (v) => {\n if (skipNextAnimation) {\n skipNextAnimation = false\n value.jump(parseValue(v, unit) as T, false)\n } else {\n value.set(parseValue(v, unit) as T)\n }\n })\n\n const removeValueOnDestroy = value.on(\"destroy\", removeSourceOnChange)\n\n return () => {\n removeSourceOnChange()\n removeValueOnDestroy()\n }\n }\n\n return stopAnimation\n}\n\nfunction parseValue(v: AnyResolvedKeyframe, unit?: string) {\n return unit ? v + unit : v\n}\n\nfunction asNumber(v: AnyResolvedKeyframe) {\n return typeof v === \"number\" ? v : parseFloat(v)\n}\n"],"names":[],"mappings":"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"}