From a016bf3801404d90d3cd6ac3654cfbdc2ed40aae Mon Sep 17 00:00:00 2001 From: Yunfei He Date: Thu, 28 May 2026 14:34:31 +0800 Subject: [PATCH] refactor(runtime): drive useAnimation via shared scheduler with elapsed-time frames --- .../runtime/src/composables/useAnimation.ts | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/runtime/src/composables/useAnimation.ts b/packages/runtime/src/composables/useAnimation.ts index 0a798e1..a574b10 100644 --- a/packages/runtime/src/composables/useAnimation.ts +++ b/packages/runtime/src/composables/useAnimation.ts @@ -2,13 +2,17 @@ import { shallowRef, watch, toValue, + inject, onScopeDispose, type MaybeRefOrGetter, type ShallowRef, } from "vue"; - -const DEFAULT_INTERVAL = 100; -const MAX_TIMER_INTERVAL = 2_147_483_647; +import { + createAnimationScheduler, + normalizeInterval, + type AnimationScheduler, +} from "../animation-scheduler.ts"; +import { AnimationSchedulerKey } from "../context.ts"; export interface AnimationOptions { /** @@ -51,11 +55,6 @@ export interface AnimationResult { readonly reset: () => void; } -function normalizeInterval(interval: number | undefined): number { - if (interval === undefined || !Number.isFinite(interval)) return DEFAULT_INTERVAL; - return Math.min(Math.max(1, Math.round(interval)), MAX_TIMER_INTERVAL); -} - /** * A composable that drives animations. Returns a frame counter, elapsed time, * frame delta, and a reset function. @@ -78,14 +77,18 @@ export function useAnimation(options: AnimationOptions = {}): AnimationResult { const time = shallowRef(0); const delta = shallowRef(0); - let timer: ReturnType | undefined; + const interval = normalizeInterval(options.interval); + // Fall back to a local standalone scheduler when used outside a vue-tui + // render tree (graceful degradation, not a silent break). + const scheduler: AnimationScheduler = + inject(AnimationSchedulerKey, null) ?? createAnimationScheduler(); + + let handle: { startTime: number; unsubscribe: () => void } | undefined; let startTime = 0; let lastTickTime = 0; - const currentInterval = normalizeInterval(options.interval); - function tick() { - const now = performance.now(); - frame.value++; + function tick(now: number) { + frame.value = Math.floor((now - startTime) / interval); time.value = now - startTime; delta.value = now - lastTickTime; lastTickTime = now; @@ -93,24 +96,23 @@ export function useAnimation(options: AnimationOptions = {}): AnimationResult { function start() { stop(); - startTime = performance.now(); - lastTickTime = startTime; frame.value = 0; time.value = 0; delta.value = 0; - timer = setInterval(tick, currentInterval); + handle = scheduler.subscribe(tick, interval); + startTime = handle.startTime; + lastTickTime = handle.startTime; } function stop() { - if (timer !== undefined) { - clearInterval(timer); - timer = undefined; + if (handle) { + handle.unsubscribe(); + handle = undefined; } } function reset() { - const wasActive = timer !== undefined; - stop(); + const wasActive = handle !== undefined; frame.value = 0; time.value = 0; delta.value = 0;