{"version":3,"file":"FollowAnimation.mjs","sources":["../../../src/animation/FollowAnimation.ts"],"sourcesContent":["import { cancelFrame, frame } from \"../frameloop\"\nimport { time } from \"../frameloop/sync-time\"\nimport { FrameData } from \"../frameloop/types\"\nimport { DriverControls } from \"./drivers/types\"\nimport { calcGeneratorVelocity } from \"./generators/utils/velocity\"\nimport { resolveStartTime } from \"./utils/resolve-start-time\"\nimport {\n GeneratorFactory,\n KeyframeGenerator,\n MotionValueAnimation,\n ValueAnimationOptions,\n} from \"./types\"\nimport { WithPromise } from \"./utils/WithPromise\"\n\n/**\n * Every running follower ticks from this one frame callback.\n */\nconst active = new Set()\n\nconst tickAll = ({ timestamp }: FrameData) => {\n active.forEach((animation) => animation.tick(timestamp))\n}\n\n/**\n * A lean animation for values that follow a target (`followValue`,\n * `springValue`): a single numeric generator, retargeted in place as\n * the target moves and ticked from a frame callback shared by every\n * follower. Only implements the controls `MotionValue.animation`\n * exposes. Repeat options are ignored: a follower only ever heads for\n * its latest target.\n */\nexport class FollowAnimation\n extends WithPromise\n implements MotionValueAnimation\n{\n state: AnimationPlayState = \"idle\"\n\n private generator: KeyframeGenerator\n\n private factory: GeneratorFactory\n\n private options: ValueAnimationOptions\n\n private startTime = 0\n\n private currentTime = 0\n\n /**\n * Whether the first tick has fired `onPlay`.\n */\n private started = false\n\n /**\n * A target set since the last tick, applied at the next one so any\n * number of sets per frame cost one retarget.\n */\n private hasNextTarget = false\n\n private nextTarget = 0\n\n private nextVelocity?: number\n\n /**\n * Only created when a custom driver is passed, otherwise the\n * follower ticks from the shared frame callback.\n */\n private driver?: DriverControls\n\n constructor(options: ValueAnimationOptions) {\n super()\n\n this.options = options\n this.factory = options.type as GeneratorFactory\n this.generator = this.factory(options)\n\n const { driver } = options\n if (driver) this.driver = driver((timestamp) => this.tick(timestamp))\n\n this.startTime = this.now()\n this.state = \"running\"\n\n if (this.driver) {\n this.driver.start()\n } else {\n active.size || frame.update(tickAll, true)\n active.add(this)\n }\n }\n\n /**\n * Aim at a new target from wherever the animation has reached by the\n * next tick. `velocity` fixes the velocity to steer with, otherwise\n * the generator's is used.\n */\n setTarget(target: number, velocity?: number) {\n this.nextTarget = target\n this.nextVelocity = velocity\n this.hasNextTarget = true\n }\n\n /**\n * Steer towards new keyframes from the current position and velocity.\n * Springs are updated in place; other generators are recreated.\n */\n retarget(keyframes: number[], velocity: number) {\n const { options, generator } = this\n options.keyframes = keyframes\n options.velocity = velocity\n this.startTime = this.now()\n this.currentTime = 0\n\n if (generator.retarget) {\n generator.retarget(keyframes, velocity)\n } else {\n this.generator = this.factory(options)\n }\n }\n\n tick(timestamp: number) {\n const { options, hasNextTarget: retargeted } = this\n const { delay = 0, onUpdate, onPlay } = options\n // Retargets happen on a tick, so only the first start can lag\n if (!this.started) {\n this.startTime = resolveStartTime(this.startTime, timestamp)\n }\n const elapsed = Math.round(timestamp - this.startTime) - delay\n const t = (this.currentTime = Math.max(0, elapsed))\n const state = this.generator.next(t)\n // Hold the origin through the delay, even if the generator is already at rest\n const value = elapsed < 0 ? options.keyframes[0] : state.value\n\n if (retargeted) {\n /**\n * Steer from where the current trajectory has reached this\n * frame, then render that position as the new origin.\n */\n this.hasNextTarget = false\n const { keyframes } = options\n keyframes[0] = value\n keyframes[1] = this.nextTarget\n this.retarget(\n keyframes,\n this.nextVelocity ?? this.getGeneratorVelocity()\n )\n }\n\n if (retargeted || !this.started) {\n this.started = true\n onPlay?.()\n }\n\n /**\n * Callbacks may stop this animation or set a new target, so check\n * the live state rather than what was read at the top of the tick.\n */\n if (this.state !== \"running\") return\n\n onUpdate?.(value)\n\n if (\n state.done &&\n elapsed >= 0 &&\n !retargeted &&\n !this.hasNextTarget &&\n this.state === \"running\"\n ) {\n this.notifyFinished()\n this.teardown()\n this.state = \"finished\"\n options.onComplete?.()\n }\n }\n\n getGeneratorVelocity(): number {\n return calcGeneratorVelocity(\n this.generator,\n this.currentTime,\n this.options.velocity\n )\n }\n\n /**\n * Bound to the instance so `animation.stop` can be passed around.\n */\n stop = () => {\n if (this.state === \"idle\") return\n this.teardown()\n this.options.onStop?.()\n }\n\n private now() {\n return this.driver ? this.driver.now() : time.now()\n }\n\n private teardown() {\n this.state = \"idle\"\n\n if (this.driver) {\n this.driver.stop()\n } else {\n active.delete(this)\n active.size || cancelFrame(tickAll)\n }\n }\n}\n"],"names":[],"mappings":"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"}